issue #98 save current state in parser branch
commit
93670a4bb9
@ -0,0 +1,585 @@
|
||||
# Medium Intermediate Representation (file mir.h)
|
||||
* This document describes MIR itself, API for its creation, and MIR textual representation
|
||||
* MIR textual representation is assembler like. Each directive or insn should be put on a separate line
|
||||
* In MIR textual syntax we use
|
||||
* `[]` for optional construction
|
||||
* `{}` for repeating zero or more times
|
||||
* `<>` for some informal construction description or construction already described or will be described
|
||||
|
||||
## MIR context
|
||||
* MIR API code has an implicit state called by MIR context
|
||||
* MIR context is represented by data of `MIR_context_t`
|
||||
* MIR context is created by function `MIR_context_t MIR_init (void)`
|
||||
* Every MIR API function (except for `MIR_init`) requires MIR context passed through the first argument of type `MIR_context_t`
|
||||
* You can use MIR functions in different threads without any synchronization
|
||||
if they work with different contexts in each thread
|
||||
|
||||
## MIR program
|
||||
* MIR program consists of MIR **modules**
|
||||
* To start work with MIR program, you should first call API function `MIR_init`
|
||||
* API function `MIR_finish (MIR_context_t ctx)` should be called last. It frees all internal data used to work with MIR program and all IR (insns, functions, items, and modules) created in this context
|
||||
* API function `MIR_output (MIR_context_t ctx, FILE *f)` outputs MIR textual representation of the program into given file
|
||||
* API function `MIR_scan_string (MIR_context_t ctx, const char *str)` reads textual MIR representation given by a string
|
||||
* API functions `MIR_write (MIR_context_t ctx, FILE *f)` and
|
||||
`MIR_read (MIR_context_t ctx, FILE *f)` outputs and reads
|
||||
**binary MIR representation** to/from given file. There are also
|
||||
functions `MIR_write_with_func (MIR_context_t ctx, const int
|
||||
(*writer_func) (MIR_context_t, uint8_t))` and `MIR_read_with_func
|
||||
(MIR_context_t ctx, const int (*reader_func) (MIR_context_t))` to
|
||||
output and read **binary MIR representation** through a function
|
||||
given as an argument. The reader function should return EOF as
|
||||
the end of the binary MIR representation, the writer function
|
||||
should be return the number of successfully output bytes
|
||||
* Binary MIR representation much more compact and faster to read than textual one
|
||||
|
||||
## MIR data type
|
||||
* MIR program works with the following **data types**:
|
||||
* `MIR_T_I8` and `MIR_T_U8` -- signed and unsigned 8-bit integer values
|
||||
* `MIR_T_I16` and `MIR_T_U16` -- signed and unsigned 16-bit integer values
|
||||
* `MIR_T_I32` and `MIR_T_U32` -- signed and unsigned 32-bit integer values
|
||||
* `MIR_T_I64` and `MIR_T_U64` -- signed and unsigned 64-bit integer values
|
||||
* ??? signed and unsigned 64-bit integer types in most cases
|
||||
are interchangeable as insns themselves decide how to treat
|
||||
their value
|
||||
* `MIR_T_F` and `MIR_T_D` -- IEEE single and double precision floating point values
|
||||
* `MIR_T_LD` - long double values. It is machine-dependent and can be IEEE double, x86 80-bit FP,
|
||||
or IEEE quad precision FP values
|
||||
* `MIR_T_P` -- pointer values. Depending on the target pointer value is actually 32-bit or 64-bit integer value
|
||||
* MIR textual representation of the types are correspondingly `i8`,
|
||||
`u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `f`, `d`, `p`,
|
||||
and `v`
|
||||
* Function `int MIR_int_type_p (MIR_type_t t)` returns TRUE if given type is an integer one (it includes pointer type too)
|
||||
* Function `int MIR_fp_type_p (MIR_type_t t)` returns TRUE if given type is a floating point type
|
||||
|
||||
## MIR module
|
||||
* Module is a high level entity of MIR program
|
||||
* Module is created through API function `MIR_module_t MIR_new_module (const char *name)`
|
||||
* Module creation is finished by calling API function `MIR_finish_module`
|
||||
* You can create only one module at any given time
|
||||
* List of all created modules can be gotten by function `DLIST (MIR_module_t) *MIR_get_module_list (MIR_context_t ctx)`
|
||||
* MIR module consists of **items**. There are following **item types** (and function for their creation):
|
||||
* **Function**: `MIR_func_item`
|
||||
* **Import**: `MIR_import_item` (`MIR_item_t MIR_new_import (MIR_context_t ctx, const char *name)`)
|
||||
* **Export**: `MIR_export_item` (`MIR_item_t MIR_new_export (MIR_context_t ctx, const char *name)`)
|
||||
* **Forward declaration**: `MIR_forward_item` (`MIR_item_t MIR_new_forward (MIR_context_t ctx, const char *name)`)
|
||||
* **Prototype**: `MIR_proto_item` (`MIR_new_proto_arr`, `MIR_new_proto`, `MIR_new_vararg_proto_arr`,
|
||||
`MIR_new_vararg_proto` analogous to `MIR_new_func_arr`, `MIR_new_func`, `MIR_new_vararg_func_arr` and
|
||||
`MIR_new_vararg_func` -- see below). The only difference is that
|
||||
two or more prototype argument names can be the same
|
||||
* **Data**: `MIR_data_item` with optional name
|
||||
(`MIR_item_t MIR_new_data (MIR_context_t ctx, const char *name, MIR_type_t el_type, size_t nel, const void *els)`
|
||||
or `MIR_item_t MIR_new_string_data (MIR_context_t ctx, const char *name, MIR_str_t str)`)
|
||||
* **Reference data**: `MIR_ref_data_item` with optional name
|
||||
(`MIR_item_t MIR_new_ref_data (MIR_context_t ctx, const char *name, MIR_item_t item, int64_t disp)`
|
||||
* The address of the item after linking plus `disp` is used to initialize the data
|
||||
* **Expression Data**: `MIR_expr_data_item` with optional name
|
||||
(`MIR_item_t MIR_new_expr_data (MIR_context_t ctx, const char *name, MIR_item_func_item)`)
|
||||
* Not all MIR functions can be used for expression data. The expression function should have
|
||||
only one result, have no arguments, not use any call or any instruction with memory
|
||||
* The expression function is called during linking and its result is used to initialize the data
|
||||
* **Memory segment**: `MIR_bss_item` with optional name (`MIR_item_t MIR_new_bss (MIR_context_t ctx, const char *name, size_t len)`)
|
||||
* Names of MIR functions, imports, and prototypes should be unique in a module
|
||||
* API functions `MIR_output_item (MIR_context_t ctx, FILE *f, MIR_item_t item)`
|
||||
and `MIR_output_module (MIR_context_t ctx, FILE *f, MIR_module_t module)` output item or module
|
||||
textual representation into given file
|
||||
* MIR text module syntax looks the following:
|
||||
```
|
||||
<module name>: module
|
||||
{<module item>}
|
||||
endmodule
|
||||
```
|
||||
|
||||
## MIR function
|
||||
* Function is an module item
|
||||
* Function has a **frame**, a stack memory reserved for each function invocation
|
||||
* Function has **local variables** (sometimes called **registers**), a part of which are **arguments**
|
||||
* A variable should have an unique name in the function
|
||||
* A variable is represented by a structure of type `MIR_var_t`
|
||||
* The structure contains variable name and its type
|
||||
* MIR function with its arguments is created through API function `MIR_item_t MIR_new_func (MIR_context_t ctx, const
|
||||
char *name, size_t nres, MIR_type_t *res_types, size_t nargs, ...)`
|
||||
or function `MIR_item_t MIR_new_func_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, MIR_var_t *arg_vars)`
|
||||
* Argument variables can be any type
|
||||
* This type only denotes how the argument value is passed
|
||||
* Any integer type argument variable has actually type `MIR_T_I64`
|
||||
* MIR functions with variable number of arguments are created through API functions
|
||||
`MIR_item_t MIR_new_vararg_func (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, ...)`
|
||||
or function `MIR_item_t MIR_new_vararg_func_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, MIR_var_t *arg_vars)`
|
||||
* `nargs` and `arg_vars` define only fixed arguments
|
||||
* MIR functions can have more one result but possible number of results
|
||||
and combination of their types are machine-defined. For example, for x86-64
|
||||
the function can have upto six results and return two integer
|
||||
values, two float or double values, and two long double values
|
||||
in any combination
|
||||
* MIR function creation is finished by calling API function `MIR_finish_func (MIR_context_t ctx)`
|
||||
* You can create only one MIR function at any given time
|
||||
* MIR text function syntax looks the following (arg-var always has a name besides type):
|
||||
```
|
||||
<function name>: func {<result type>, } [ arg-var {, <arg-var> } [, ...]]
|
||||
{<insn>}
|
||||
endfun
|
||||
```
|
||||
* Non-argument function variables are created through API function
|
||||
`MIR_reg_t MIR_new_func_reg (MIR_context_t ctx, MIR_func_t func, MIR_type_t type, const char *name)`
|
||||
* The only permitted integer type for the variable is `MIR_T_I64` (or MIR_T_U64???)
|
||||
* Names in form `t<number>` can not be used as they are fixed for internal purposes
|
||||
* You can create function variables even after finishing the
|
||||
function creation. This can be used to modify function insns,
|
||||
e.g. for optimizations
|
||||
* Non-argument variable declaration syntax in MIR textual representation looks the following:
|
||||
```
|
||||
local [ <var type>:<var name> {, <var type>:<var name>} ]
|
||||
```
|
||||
* In MIR textual representation variable should be defined through `local` before its use
|
||||
|
||||
## MIR insn operands
|
||||
* MIR insns work with operands
|
||||
* There are following operands:
|
||||
* Signed or unsigned **64-bit integer value operands** created through API functions
|
||||
`MIR_op_t MIR_new_int_op (MIR_context_t ctx, int64_t v)` and `MIR_op_t MIR_new_uint_op (MIR_context_t ctx, uint64_t v)`
|
||||
* In MIR text they are represented the same way as C integer numbers (e.g. octal, decimal, hexadecimal ones)
|
||||
* **Float, double or long double value operands** created through API functions `MIR_op_t MIR_new_float_op (MIR_context_t ctx, float v)`,
|
||||
`MIR_op_t MIR_new_double_op (MIR_context_t ctx, double v)`, and `MIR_op_t MIR_new_ldouble_op (MIR_context_t ctx, long double v)`
|
||||
* In MIR text they are represented the same way as C floating point numbers
|
||||
* **String operands** created through API functions `MIR_op_t MIR_new_str_op (MIR_context_t ctx, MIR_str_t str)`
|
||||
* In MIR text they are represented by `typedef struct MIR_str {size_t len; const char *s;} MIR_str_t`
|
||||
* Strings for each operand are put into memory (which can be modified) and the memory address actually presents the string
|
||||
* **Label operand** created through API function `MIR_op_t MIR_new_label_op (MIR_context_t ctx, MIR_label_t label)`
|
||||
* Here `label` is a special insn created by API function `MIR_insn_t MIR_new_label (MIR_context_t ctx)`
|
||||
* In MIR text, they are represented by unique label name
|
||||
* **Reference operands** created through API function `MIR_op_t MIR_new_ref_op (MIR_context_t ctx, MIR_item_t item)`
|
||||
* In MIR text, they are represented by the corresponding item name
|
||||
* **Register (variable) operands** created through API function `MIR_op_t MIR_new_reg_op (MIR_context_t ctx, MIR_reg_t reg)`
|
||||
* In MIR text they are represented by the corresponding variable name
|
||||
* Value of type `MIR_reg_t` is returned by function `MIR_new_func_reg`
|
||||
or can be gotten by function `MIR_reg_t MIR_reg (MIR_context_t ctx, const char *reg_name, MIR_func_t func)`, e.g. for argument-variables
|
||||
* **Memory operands** consists of type, displacement, base
|
||||
register, index register and index scale. Memory operand is
|
||||
created through API function `MIR_op_t MIR_new_mem_op (MIR_context_t ctx, MIR_type_t type,
|
||||
MIR_disp_t disp, MIR_reg_t base, MIR_reg_t index, MIR_scale_t
|
||||
scale)`
|
||||
* The arguments define address of memory as `disp + base + index * scale`
|
||||
* Integer type input memory is transformed to 64-bit integer value with sign or zero extension
|
||||
depending on signedness of the type
|
||||
* result 64-bit integer value is truncated to integer memory type
|
||||
* Memory operand has the following syntax in MIR text (absent displacement means zero one,
|
||||
absent scale means one, scale should be 1, 2, 4, or 8):
|
||||
|
||||
```
|
||||
<type>: <disp>
|
||||
<type>: [<disp>] (<base reg> [, <index reg> [, <scale> ]])
|
||||
```
|
||||
* API function `MIR_output_op (MIR_context_t ctx, FILE *f, MIR_op_t op, MIR_func_t func)` outputs the operand
|
||||
textual representation into given file
|
||||
|
||||
|
||||
## MIR insns
|
||||
* All MIR insns (but call or ret one) expects fixed number of operands
|
||||
* Most MIR insns are 3-operand insns: two inputs and one output
|
||||
* In majority cases **the first insn operand** describes where the insn result (if any) will be placed
|
||||
* Only register or memory operand can be insn output (result) operand
|
||||
* MIR insn can be created through API functions `MIR_insn_t MIR_new_insn (MIR_context_t ctx, MIR_insn_code_t code, ...)`
|
||||
and `MIR_insn_t MIR_new_insn_arr (MIR_context_t ctx, MIR_insn_code_t code, size_t nops, MIR_op_t *ops)`
|
||||
* Number of operands and their types should be what is expected by the insn being created
|
||||
* You can not use `MIR_new_insn` for the creation of call and ret insns as these insns have a variable number of operands.
|
||||
To create such insns you should use `MIR_new_insn_arr` or special functions
|
||||
`MIR_insn_t MIR_new_call_insn (MIR_context_t ctx, size_t nops, ...)` and `MIR_insn_t MIR_new_ret_insn (MIR_context_t ctx, size_t nops, ...)`
|
||||
* You can get insn name and number of insn operands through API functions
|
||||
`const char *MIR_insn_name (MIR_context_t ctx, MIR_insn_code_t code)` and `size_t MIR_insn_nops (MIR_context_t ctx, MIR_insn_t insn)`
|
||||
* You can add a created insn at the beginning or end of function insn list through API functions
|
||||
`MIR_prepend_insn (MIR_context_t ctx, MIR_item_t func, MIR_insn_t insn)` and `MIR_append_insn (MIR_context_t ctx, MIR_item_t func, MIR_insn_t insn)`
|
||||
* You can insert a created insn in the middle of function insn list through API functions
|
||||
`MIR_insert_insn_after (MIR_context_t ctx, MIR_item_t func, MIR_insn_t after, MIR_insn_t insn)` and
|
||||
`MIR_insert_insn_before (MIR_context_t ctx, MIR_item_t func, MIR_insn_t before, MIR_insn_t insn)`
|
||||
* The insn `after` and `before` should be already in the list
|
||||
* You can remove insn from the function list through API function `MIR_remove_insn (MIR_context_t ctx, MIR_item_t func, MIR_insn_t insn)`
|
||||
* The insn should be not inserted in the list if it is already there
|
||||
* The insn should be not removed form the list if it is not there
|
||||
* API function `MIR_output_insn (MIR_context_t ctx, FILE *f, MIR_insn_t insn, MIR_func_t func, int newline_p)` outputs the insn
|
||||
textual representation into given file with a newline at the end depending on value of `newline_p`
|
||||
* Insn has the following syntax in MIR text:
|
||||
```
|
||||
{<label name>:} [<insn name> <operand> {, <operand>}]
|
||||
```
|
||||
* More one insn can be put on the same line by separating the insns by `;`
|
||||
|
||||
### MIR move insns
|
||||
* There are following MIR move insns:
|
||||
|
||||
| Insn Code | Nops | Description |
|
||||
|-------------------------|-----:|--------------------------------------------------------|
|
||||
| `MIR_MOV` | 2 | move 64-bit integer values |
|
||||
| `MIR_FMOV` | 2 | move **single precision** floating point values |
|
||||
| `MIR_DMOV` | 2 | move **double precision** floating point values |
|
||||
| `MIR_LDMOV` | 2 | move **long double** floating point values |
|
||||
|
||||
### MIR integer insns
|
||||
* If insn has suffix `S` in insn name, the insn works with lower 32-bit part of 64-bit integer value
|
||||
* The higher part of 32-bit insn result is undefined
|
||||
* If insn has prefix `U` in insn name, the insn treats integer as unsigned integers
|
||||
* Some insns has no unsigned variant as MIR is oriented to CPUs with two complement integer arithmetic
|
||||
(the huge majority of all CPUs)
|
||||
|
||||
| Insn Code | Nops | Description |
|
||||
|-------------------------|-----:|--------------------------------------------------------|
|
||||
| `MIR_EXT8` | 2 | **sign** extension of lower **8 bit** input part |
|
||||
| `MIR_UEXT8` | 2 | **zero** extension of lower **8 bit** input part |
|
||||
| `MIR_EXT16` | 2 | **sign** extension of lower **16 bit** input part |
|
||||
| `MIR_UEXT16` | 2 | **zero** extension of lower **16 bit** input part |
|
||||
| `MIR_EXT32` | 2 | **sign** extension of lower **32 bit** input part |
|
||||
| `MIR_UEXT32` | 2 | **zero** extension of lower **32 bit** input part |
|
||||
| | | |
|
||||
| `MIR_NEG` | 2 | changing sign of **64-bit* integer value |
|
||||
| `MIR_NEGS` | 2 | changing sign of **32-bit* integer value |
|
||||
| | | |
|
||||
| `MIR_ADD`, `MIR_SUB` | 3 | **64-bit** integer addition and subtraction |
|
||||
| `MIR_ADDS`, `MIR_SUBS` | 3 | **32-bit** integer addition and subtraction |
|
||||
| `MIR_MUL`, `MIR_DIV` | 3 | **64-bit signed** multiplication and divison |
|
||||
| `MIR_UMUL`, `MIR_UDIV` | 3 | **64-bit unsigned** integer multiplication and divison |
|
||||
| `MIR_MULS`, `MIR_DIVS` | 3 | **32-bit signed** multiplication and divison |
|
||||
| `MIR_UMULS`, `MIR_UDIVS`| 3 | **32-bit unsigned** integer multiplication and divison |
|
||||
| `MIR_MOD` | 3 | **64-bit signed** modulo operation |
|
||||
| `MIR_UMOD` | 3 | **64-bit unsigned** integer modulo operation |
|
||||
| `MIR_MODS` | 3 | **32-bit signed** modulo operation |
|
||||
| `MIR_UMODS` | 3 | **32-bit unsigned** integer modulo operation |
|
||||
| | | |
|
||||
| `MIR_AND`, `MIR_OR` | 3 | **64-bit** integer bitwise AND and OR |
|
||||
| `MIR_ANDS`, `MIR_ORS` | 3 | **32-bit** integer bitwise AND and OR |
|
||||
| `MIR_XOR` | 3 | **64-bit** integer bitwise XOR |
|
||||
| `MIR_XORS` | 3 | **32-bit** integer bitwise XOR |
|
||||
| | | |
|
||||
| `MIR_LSH` | 3 | **64-bit** integer left shift |
|
||||
| `MIR_LSHS` | 3 | **32-bit** integer left shift |
|
||||
| `MIR_RSH` | 3 | **64-bit** integer right shift with **sign** extension |
|
||||
| `MIR_RSHS` | 3 | **32-bit** integer right shift with **sign** extension |
|
||||
| `MIR_URSH` | 3 | **64-bit** integer right shift with **zero** extension |
|
||||
| `MIR_URSHS` | 3 | **32-bit** integer right shift with **zero** extension |
|
||||
| | | |
|
||||
| `MIR_EQ`, `MIR_NE` | 3 | equality/inequality of **64-bit** integers |
|
||||
| `MIR_EQS`, `MIR_NES` | 3 | equality/inequality of **32-bit** integers |
|
||||
| `MIR_LT`, `MIR_LE` | 3 | **64-bit signed** less than/less than or equal |
|
||||
| `MIR_ULT`, `MIR_ULE` | 3 | **64-bit unsigned** less than/less than or equal |
|
||||
| `MIR_LTS`, `MIR_LES` | 3 | **32-bit signed** less than/less than or equal |
|
||||
| `MIR_ULTS`, `MIR_ULES` | 3 | **32-bit unsigned** less than/less than or equal |
|
||||
| `MIR_GT`, `MIR_GE` | 3 | **64-bit signed** greater than/greater than or equal |
|
||||
| `MIR_UGT`, `MIR_UGE` | 3 | **64-bit unsigned** greater than/greater than or equal |
|
||||
| `MIR_GTS`, `MIR_GES` | 3 | **32-bit signed** greater than/greater than or equal |
|
||||
| `MIR_UGTS`, `MIR_UGES` | 3 | **32-bit unsigned** greater than/greater than or equal |
|
||||
|
||||
### MIR floating point insns
|
||||
* If insn has prefix `F` in insn name, the insn is single precision float point insn. Its operands should have `MIR_T_F` type
|
||||
* If insn has prefix `D` in insn name, the insn is double precision float point insn. Its operands should have `MIR_T_D` type
|
||||
* Otherwise, insn has prefix `LD` in insn name and the insn is a long double insn.
|
||||
Its operands should have `MIR_T_LD` type.
|
||||
* The result of comparison insn is a 64-bit integer value, so the result operand should be of integer type
|
||||
|
||||
| Insn Code | Nops | Description |
|
||||
|--------------------------------------|-----:|-----------------------------------------------------------------|
|
||||
| `MIR_F2I`, `MIR_D2I`, `MIR_LD2I` | 2 | transforming floating point value into 64-bit integer |
|
||||
| `MIR_F2D` | 2 | transforming single to double precision FP value |
|
||||
| `MIR_F2LD` | 2 | transforming single precision to long double FP value |
|
||||
| `MIR_D2F` | 2 | transforming double to single precision FP value |
|
||||
| `MIR_D2LD` | 2 | transforming double precision to long double FP value |
|
||||
| `MIR_LD2F` | 2 | transforming long double to single precision FP value |
|
||||
| `MIR_LD2D` | 2 | transforming long double to double precision FP value |
|
||||
| `MIR_I2F`, `MIR_I2D`, `MIR_I2LD` | 2 | transforming 64-bit integer into a floating point value |
|
||||
| `MIR_UI2F`, `MIR_UI2D`, `MIR_UI2LD` | 2 | transforming unsigned 64-bit integer into a floating point value|
|
||||
| `MIR_FNEG`, `MIR_DNEG`, `MIR_LDNEG` | 2 | changing sign of floating point value |
|
||||
| `MIR_FADD`, `MIR_FSUB` | 3 | **single** precision addition and subtraction |
|
||||
| `MIR_DADD`, `MIR_DSUB` | 3 | **double** precision addition and subtraction |
|
||||
| `MIR_LDADD`, `MIR_LDSUB` | 3 | **long double** addition and subtraction |
|
||||
| `MIR_FMUL`, `MIR_FDIV` | 3 | **single** precision multiplication and divison |
|
||||
| `MIR_DMUL`, `MIR_DDIV` | 3 | **double** precision multiplication and divison |
|
||||
| `MIR_LDMUL`, `MIR_LDDIV` | 3 | **long double** multiplication and divison |
|
||||
| `MIR_FEQ`, `MIR_FNE` | 3 | equality/inequality of **single** precision values |
|
||||
| `MIR_DEQ`, `MIR_DNE` | 3 | equality/inequality of **double** precision values |
|
||||
| `MIR_LDEQ`, `MIR_LDNE` | 3 | equality/inequality of **long double** values |
|
||||
| `MIR_FLT`, `MIR_FLE` | 3 | **single** precision less than/less than or equal |
|
||||
| `MIR_DLT`, `MIR_DLE` | 3 | **double** precision less than/less than or equal |
|
||||
| `MIR_LDLT`, `MIR_LDLE` | 3 | **long double** less than/less than or equal |
|
||||
| `MIR_FGT`, `MIR_FGE` | 3 | **single** precision greater than/greater than or equal |
|
||||
| `MIR_DGT`, `MIR_DGE` | 3 | **double** precision greater than/greater than or equal |
|
||||
| `MIR_LDGT`, `MIR_LDGE` | 3 | **long double** greater than/greater than or equal |
|
||||
|
||||
### MIR branch insns
|
||||
* The first operand of the insn should be label
|
||||
|
||||
| Insn Code | Nops | Description |
|
||||
|-------------------------|-----:|---------------------------------------------------------------|
|
||||
| `MIR_JMP` | 1 | unconditional jump to the label |
|
||||
| `MIR_BT` | 2 | jump to the label when 2nd **64-bit** operand is **nonzero** |
|
||||
| `MIR_BTS` | 2 | jump to the label when 2nd **32-bit** operand is **nonzero** |
|
||||
| `MIR_BF` | 2 | jump to the label when 2nd **64-bit** operand is **zero** |
|
||||
| `MIR_BFS` | 2 | jump to the label when 2nd **32-bit** operand is **zero** |
|
||||
|
||||
### MIR switch insn
|
||||
* The first operand of `MIR_SWITCH` insn should have an integer value from 0 to `N - 1` inclusive
|
||||
* The rest operands should be `N` labels, where `N > 0`
|
||||
* Execution of the insn will be an jump on the label corresponding to the first operand value
|
||||
* If the first operand value is out of the range of permitted values, the execution result is undefined
|
||||
|
||||
### MIR integer comparison and branch insn
|
||||
* The first operand of the insn should be label. Label will be the next executed insn if the result of comparison is non-zero
|
||||
|
||||
| Insn Code | Nops | Description |
|
||||
|-------------------------|-----:|---------------------------------------------------------------|
|
||||
| `MIR_BEQ`, `MIR_BNE` | 3 | jump on **64-bit** equality/inequality |
|
||||
| `MIR_BEQS`, `MIR_BNES` | 3 | jump on **32-bit** equality/inequality |
|
||||
| `MIR_BLT`, `MIR_BLE` | 3 | jump on **signed 64-bit** less than/less than or equal |
|
||||
| `MIR_UBLT`, `MIR_UBLE` | 3 | jump on **unsigned 64-bit** less than/less than or equal |
|
||||
| `MIR_BLTS`, `MIR_BLES` | 3 | jump on **signed 32-bit** less than/less than or equal |
|
||||
| `MIR_UBLTS`, `MIR_UBLES`| 3 | jump on **unsigned 32-bit** less than/less than or equal |
|
||||
| `MIR_BGT`, `MIR_BGE` | 3 | jump on **signed 64-bit** greater than/greater than or equal |
|
||||
| `MIR_UBGT`, `MIR_UBGE` | 3 | jump on **unsigned 64-bit** greater than/greater than or equal|
|
||||
| `MIR_BGTS`, `MIR_BGES` | 3 | jump on **signed 32-bit** greater than/greater than or equal |
|
||||
| `MIR_UBGTS`, `MIR_UBLES`| 3 | jump on **unsigned 32-bit** greater than/greater than or equal|
|
||||
|
||||
### MIR floating point comparison and branch insn
|
||||
* The first operand of the insn should be label. Label will be the next executed insn if the result of comparison is non-zero
|
||||
* See comparison semantics in the corresponding comparison insns
|
||||
|
||||
| Insn Code | Nops | Description |
|
||||
|---------------------------|-----:|----------------------------------------------------------------|
|
||||
| `MIR_FBEQ`, `MIR_FBNE` | 3 | jump on **single** precision equality/inequality |
|
||||
| `MIR_DBEQ`, `MIR_DBNE` | 3 | jump on **double** precision equality/inequality |
|
||||
| `MIR_LDBEQ`, `MIR_LDBNE` | 3 | jump on **long double** equality/inequality |
|
||||
| `MIR_FBLT`, `MIR_FBLE` | 3 | jump on **single** precision less than/less than or equal |
|
||||
| `MIR_DBLT`, `MIR_DBLE` | 3 | jump on **double** precision less than/less than or equal |
|
||||
| `MIR_LDBLT`, `MIR_LDBLE` | 3 | jump on **long double** less than/less than or equal |
|
||||
| `MIR_FBGT`, `MIR_FBGE` | 3 | jump on **single** precision greater than/greater than or equal|
|
||||
| `MIR_DBGT`, `MIR_DBGE` | 3 | jump on **double** precision greater than/less/ than or equal |
|
||||
| `MIR_LDBGT`, `MIR_LDBGE` | 3 | jump on **long double** greater than/less/ than or equal |
|
||||
|
||||
### MIR return insn
|
||||
* Return insn has zero or more operands
|
||||
* Return insn operands should correspond to return types of the function
|
||||
* 64-bit integer value is truncated to the corresponding function return type first
|
||||
* The return values will be the function call values
|
||||
|
||||
### MIR_CALL insn
|
||||
* The insn has variable number of operands
|
||||
* The first operand is a prototype reference operand
|
||||
* The second operand is a called function address
|
||||
* The prototype should correspond MIR function definition if function address represents a MIR function
|
||||
* The prototype should correspond C function definition if the address is C function address
|
||||
* If the prototype has *N* return types, the next *N* operands are
|
||||
output operands which will contain the result values of the function
|
||||
call
|
||||
* The subsequent operands are arguments. Their types and number and should be the same as in the prototype
|
||||
* Integer arguments are truncated according to integer prototype argument type
|
||||
|
||||
### MIR_INLINE insn
|
||||
* This insn is analogous to `MIR_CALL` but after linking this insn
|
||||
will be changed by inlined function body if it is possible
|
||||
* Calls of vararg functions are never inlined
|
||||
|
||||
### MIR_ALLOCA insn
|
||||
* Reserve memory on the stack whose size is given as the 2nd operand and assign the memory address to the 1st operand
|
||||
* The reserved memory will be aligned according target ABI
|
||||
|
||||
### MIR_BSTART and MIR_BEND insns
|
||||
* MIR users can use them implement blocks with automatic
|
||||
deallocation of memory allocated by `MIR_ALLOCA` inside the
|
||||
blocks. But mostly these insns are used to implement call
|
||||
inlining of functions using alloca
|
||||
* The both insns use one operand
|
||||
* The first insn saves the stack pointer in the operand
|
||||
* The second insn restores stack pointer from the operand
|
||||
|
||||
### MIR_VA_START, MIR_VA_ARG, and MIR_VA_END insns
|
||||
* These insns are only for variable number arguments functions
|
||||
* `MIR_VA_START` and `MIR_VA_END` have one input operand, an address
|
||||
of va_list structure (see C stdarg.h for more details). Unlike C
|
||||
va_start, MIR_VA_START just takes one parameter
|
||||
* `MIR_VA_ARG` takes va_list and any memory operand and returns
|
||||
address of the next argument in the 1st insn operand. The memory
|
||||
operand type defines the type of the argument
|
||||
* va_list operand can be memory with undefined type. In this case
|
||||
address of the va_list is not in the memory but is the
|
||||
memory address
|
||||
|
||||
## MIR API example
|
||||
* The following code on C creates MIR analog of C code
|
||||
`int64_t loop (int64_t arg1) {int64_t count = 0; while (count < arg1) count++; return count;}`
|
||||
```
|
||||
MIR_module_t m = MIR_new_module (ctx, "m");
|
||||
MIR_item_t func = MIR_new_func (ctx, "loop", MIR_T_I64, 1, MIR_T_I64, "arg1");
|
||||
MIR_reg_t COUNT = MIR_new_func_reg (ctx, func->u.func, MIR_T_I64, "count");
|
||||
MIR_reg_t ARG1 = MIR_reg (ctx, "arg1", func->u.func);
|
||||
MIR_label_t fin = MIR_new_label (ctx), cont = MIR_new_label (ctx);
|
||||
|
||||
MIR_append_insn (ctx, func, MIR_new_insn (ctx, MIR_MOV, MIR_new_reg_op (ctx, COUNT),
|
||||
MIR_new_int_op (ctx, 0)));
|
||||
MIR_append_insn (ctx, func, MIR_new_insn (ctx, MIR_BGE, MIR_new_label_op (ctx, fin),
|
||||
MIR_new_reg_op (ctx, COUNT), MIR_new_reg_op (ctx, ARG1)));
|
||||
MIR_append_insn (ctx, func, cont);
|
||||
MIR_append_insn (ctx, func, MIR_new_insn (ctx, MIR_ADD, MIR_new_reg_op (ctx, COUNT),
|
||||
MIR_new_reg_op (ctx, COUNT), MIR_new_int_op (ctx, 1)));
|
||||
MIR_append_insn (ctx, func, MIR_new_insn (ctx, MIR_BLT, MIR_new_label_op (ctx, cont),
|
||||
MIR_new_reg_op (ctx, COUNT), MIR_new_reg_op (ctx, ARG1)));
|
||||
MIR_append_insn (ctx, func, fin);
|
||||
MIR_append_insn (ctx, func, MIR_new_ret_insn (ctx, 1, MIR_new_reg_op (ctx, COUNT)));
|
||||
MIR_finish_func (ctx);
|
||||
MIR_finish_module (ctx);
|
||||
```
|
||||
|
||||
## MIR text example
|
||||
|
||||
```
|
||||
m_sieve: module
|
||||
export sieve
|
||||
sieve: func i32, i32:N
|
||||
local i64:iter, i64:count, i64:i, i64:k, i64:prime, i64:temp, i64:flags
|
||||
alloca flags, 819000
|
||||
mov iter, 0
|
||||
loop: bge fin, iter, N
|
||||
mov count, 0; mov i, 0
|
||||
loop2: bge fin2, i, 819000
|
||||
mov u8:(flags, i), 1; add i, i, 1
|
||||
jmp loop2
|
||||
fin2: mov i, 0
|
||||
loop3: bge fin3, i, 819000
|
||||
beq cont3, u8:(flags,i), 0
|
||||
add temp, i, i; add prime, temp, 3; add k, i, prime
|
||||
loop4: bge fin4, k, 819000
|
||||
mov u8:(flags, k), 0; add k, k, prime
|
||||
jmp loop4
|
||||
fin4: add count, count, 1
|
||||
cont3: add i, i, 1
|
||||
jmp loop3
|
||||
fin3: add iter, iter, 1
|
||||
jmp loop
|
||||
fin: rets count
|
||||
endfunc
|
||||
endmodule
|
||||
m_ex100: module
|
||||
format: string "sieve (10) = %d\n"
|
||||
p_printf: proto p:fmt, i32:v
|
||||
p_seive: proto i32, i32:iter
|
||||
export ex100
|
||||
import sieve, printf
|
||||
ex100: func v
|
||||
local i64:r
|
||||
call p_sieve, sieve, r, 100
|
||||
call p_printf, printf, format, r
|
||||
endfunc
|
||||
endmodule
|
||||
```
|
||||
|
||||
## Other MIR API functions
|
||||
* MIR API can find a lot of errors. They are reported through a
|
||||
error function of type `void (*MIR_error_func_t) (MIR_context ctx, MIR_error_type_t
|
||||
error_type, const char *message)`. The function is considered to
|
||||
never return. To see all error types, please look at the
|
||||
definition of error type `MIR_error_type_t` in file mir.h
|
||||
* You can get and set up the current error function through API
|
||||
functions `MIR_error_func_t MIR_get_error_func (MIR_context ctx)` and `MIR_set_error_func
|
||||
(MIR_context ctx, MIR_error_func_t func)`.
|
||||
* The default error function prints the message into stderr and call `exit (1)`
|
||||
* MIR is pretty flexible and can describe complex insns, e.g. insns
|
||||
whose all operands are memory. Sometimes you need a very simple
|
||||
form of MIR representation. During load of module all its functions are simplified as much
|
||||
as possible by adding new insns and registers resulting in a form in which:
|
||||
* immediate, memory, reference operands can be used only in move insns
|
||||
* memory have only base register (no displacement and index register)
|
||||
* string and float immediate operands (if `mem_float_p`) are changed onto
|
||||
references for new string and data items
|
||||
* Before execution of MIR code (through interpreter or machine code generated by JIT),
|
||||
you need to load and link it
|
||||
* You can load MIR module through API function `MIR_load_module
|
||||
(MIR_context ctx, MIR_module_t m)`. The function simplifies module code.
|
||||
It also allocates the module data/bss
|
||||
and makes visible the exported module items to other module
|
||||
during subsequent linking. There is a guarantee that the
|
||||
different data/bss items will be in adjacent memory if the
|
||||
data/bss items go one after another and all the data/bss items
|
||||
except the first one are anonymous (it means they have no name).
|
||||
Such adjacent data/bss items are called a **section**.
|
||||
Alignment of the section is malloc alignment. There are no any
|
||||
memory space between data/bss in the section. If you need to
|
||||
provide necessary alignment of a data/bss in the section you
|
||||
should do it yourself by putting additional anonymous data/bss
|
||||
before given data/bss if it is necessary. BSS memory is
|
||||
initialized by zero and data memory is initialized by the
|
||||
corresponding data. If there is already an exported item with
|
||||
the same name, it will be not visible for linking anymore. Such
|
||||
visibility mechanism permits usage of different versions of the
|
||||
same function
|
||||
* Reference data are initialized not during loading but during linking after
|
||||
the referenced item address is known. The address is used for the data
|
||||
initialization
|
||||
* Expression data are also initialized not during loading but during linking after
|
||||
all addresses are known. The expression function is evaluated by the interpreter
|
||||
and its evaluation result is used for the data initialization. For example, if
|
||||
you need to initialize data by item address plus offset you should use
|
||||
an expression data
|
||||
* MIR permits to use imported items not implemented in MIR, for
|
||||
example to use C standard function `strcmp`. You need to inform
|
||||
MIR about it. API function `MIR_load_external (MIR_context ctx, const char
|
||||
*name, void *addr)` informs that imported items with given name
|
||||
have given address (e.g. C function address or data)
|
||||
* Imports/exports of modules loaded since the last link can be
|
||||
linked through API function `MIR_link (MIR_context ctx, void (*set_interface) (MIR_item_t item),
|
||||
void * (*import_resolver) (const char *))`
|
||||
* `MIR_link` function inlines most `MIR_INLINE` calls
|
||||
* `MIR_link` function also sets up call interface
|
||||
* If you pass `MIR_set_interp_interface` to `MIR_link`, then
|
||||
called functions from MIR code will be interpreted
|
||||
* If you pass `MIR_set_gen_interface` to `MIR_link`, then
|
||||
MIR-generator will generate machine code for all loaded MIR
|
||||
functions and called functions from MIR code will execute the
|
||||
machine code
|
||||
* If you pass `MIR_set_lazy_gen_interface` to `MIR_link`, then
|
||||
MIR-generator will generate machine code only on the first
|
||||
function call and called functions from MIR code will execute
|
||||
the machine code
|
||||
* If you pass non-null `import_resolver` function, it will be
|
||||
called for defining address for import without definition.
|
||||
The function get the import name and return the address which
|
||||
will be used for the import item. This function can be useful
|
||||
for searching `dlopen` library symbols when use of
|
||||
MIR_load_external is not convenient
|
||||
|
||||
# MIR code execution
|
||||
* Linked MIR code can be executed by an **interpreter** or machine code generated by **MIR generator**
|
||||
|
||||
# MIR code interpretation
|
||||
* The interpreter is an obligatory part of MIR API because it can be used during linking
|
||||
* The interpreter is automatically initialized and finished with MIR API initialization and finishing
|
||||
* The interpreter works with values represented by type `MIR_val_t` which is union
|
||||
`union {..., int64_t i; uint64_t u; float f; double d; long double d;}`
|
||||
* You can execute a MIR function code by API functions `void
|
||||
MIR_interp (MIR_context ctx, MIR_item_t func_item, MIR_val_t *results, size_t nargs, ...)` and
|
||||
`void MIR_interp_arr (MIR_context ctx, MIR_item_t func_item, MIR_val_t *results, size_t nargs,
|
||||
MIR_val_t *vals)`
|
||||
* The function results are returned through parameter `results`. You should pass
|
||||
a container of enough size to return all function results.
|
||||
* You can execute a MIR function code also through C function call
|
||||
mechanism. First you need to setup the C function interface
|
||||
through API function `MIR_set_interp_interface (MIR_context ctx, MIR_item_t
|
||||
func_item)`. After that you can `func_item->addr` to call the
|
||||
MIR function as usual C function
|
||||
* C function interface is implemented by generation of machine
|
||||
code specialized for MIR function. Therefore the interface
|
||||
works only on the same targets as MIR generator
|
||||
|
||||
# MIR generator (file mir-gen.h)
|
||||
* Before use of MIR generator you should initialize it by API function `MIR_gen_init (MIR_context ctx)`
|
||||
* API function `MIR_gen_finish (MIR_context ctx)` should be called last after any generator usage.
|
||||
It frees all internal generator data
|
||||
* API function `void *MIR_gen (MIR_context ctx, MIR_item_t func_item)` generates machine code of given MIR function
|
||||
and returns an address to call it. You can call the code as usual C function by using this address
|
||||
as the called function address
|
||||
* API function `void MIR_gen_set_debug_file (MIR_context_t ctx, FILE *f)` sets up MIR generator debug file to `f`.
|
||||
If it is not NULL a lot of debugging and optimization information will be output to the file. It is useful mostly
|
||||
for MIR developers
|
||||
* API function `void MIR_gen_set_optimize_level (MIR_context_t ctx, unsigned int level)` sets up optimization
|
||||
level for MIR generator:
|
||||
* `0` means only register allocator and machine code generator work
|
||||
* `1` means additional code selection task. On this level MIR generator creates more compact and faster
|
||||
code than on zero level with practically on the same speed
|
||||
* `2` means additionally common sub-expression elimination and sparse conditional constant propagation.
|
||||
This is a default level. This level is valuable if you generate bad input MIR code with a lot redundancy
|
||||
and constants. The generation speed on level `1` is about 50% faster than on level `2`
|
||||
* `3` means additionally register renaming and loop invariant code motion. The generation speed
|
||||
on level `2` is about 50% faster than on level `3`
|
@ -0,0 +1,23 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
|
||||
#include "../mirc.h"
|
||||
#include "mirc-aarch64-linux.h"
|
||||
|
||||
static const char *standard_includes[] = {mirc, aarch64_mirc};
|
||||
|
||||
static const char *standard_include_dirs[] = {"include/mirc/", "include/mirc/aarch64/"};
|
||||
|
||||
#define MAX_ALIGNMENT 16
|
||||
|
||||
#define ADJUST_VAR_ALIGNMENT(c2m_ctx, align, type) \
|
||||
aarch64_adjust_var_alignment (c2m_ctx, align, type)
|
||||
|
||||
static int aarch64_adjust_var_alignment (c2m_ctx_t c2m_ctx, int align, struct type *type) {
|
||||
return align;
|
||||
}
|
||||
|
||||
static int invalid_alignment (mir_llong align) {
|
||||
return align != 0 && align != 1 && align != 2 && align != 4 && align != 8 && align != 16;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MIR_CHAR_BIT 8
|
||||
|
||||
typedef int8_t mir_schar;
|
||||
typedef int16_t mir_short;
|
||||
typedef int32_t mir_int;
|
||||
typedef int64_t mir_long;
|
||||
typedef int64_t mir_llong;
|
||||
|
||||
#define MIR_SCHAR_MIN INT8_MIN
|
||||
#define MIR_SCHAR_MAX INT8_MAX
|
||||
#define MIR_SHORT_MIN INT16_MIN
|
||||
#define MIR_SHORT_MAX INT16_MAX
|
||||
#define MIR_INT_MIN INT32_MIN
|
||||
#define MIR_INT_MAX INT32_MAX
|
||||
#define MIR_LONG_MIN INT64_MIN
|
||||
#define MIR_LONG_MAX INT64_MAX
|
||||
#define MIR_LLONG_MIN INT64_MIN
|
||||
#define MIR_LLONG_MAX INT64_MAX
|
||||
|
||||
typedef uint8_t mir_uchar;
|
||||
typedef uint16_t mir_ushort;
|
||||
typedef uint32_t mir_uint;
|
||||
typedef uint64_t mir_ulong;
|
||||
typedef uint64_t mir_ullong;
|
||||
|
||||
#define MIR_UCHAR_MAX UINT8_MAX
|
||||
#define MIR_USHORT_MAX UINT16_MAX
|
||||
#define MIR_UINT_MAX UINT32_MAX
|
||||
#define MIR_ULONG_MAX UINT64_MAX
|
||||
#define MIR_ULLONG_MAX UINT64_MAX
|
||||
|
||||
typedef mir_schar mir_char;
|
||||
#define MIR_CHAR_MIN MIR_SCHAR_MIN
|
||||
#define MIR_CHAR_MAX MIR_SCHAR_MAX
|
||||
|
||||
typedef float mir_float;
|
||||
typedef double mir_double;
|
||||
typedef long double mir_ldouble;
|
||||
|
||||
typedef uint8_t mir_bool;
|
||||
typedef int64_t mir_ptrdiff_t;
|
||||
typedef uint64_t mir_size_t;
|
||||
|
||||
#define MIR_SIZE_MAX UINT64_MAX
|
@ -0,0 +1,93 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
|
||||
static char aarch64_mirc[]
|
||||
= "#define __aarch64__ 1\n"
|
||||
"#define _LP64 1\n"
|
||||
"#define __LP64__ 1\n"
|
||||
"#define __ARM_ARCH 8\n"
|
||||
"\n"
|
||||
"#define __SIZEOF_DOUBLE__ 8\n"
|
||||
"#define __SIZEOF_FLOAT__ 4\n"
|
||||
"#define __SIZEOF_INT__ 4\n"
|
||||
"#define __SIZEOF_LONG_DOUBLE__ 16\n"
|
||||
"#define __SIZEOF_LONG_LONG__ 8\n"
|
||||
"#define __SIZEOF_LONG__ 8\n"
|
||||
"#define __SIZEOF_POINTER__ 8\n"
|
||||
"#define __SIZEOF_PTRDIFF_T__ 8\n"
|
||||
"#define __SIZEOF_SHORT__ 2\n"
|
||||
"#define __SIZEOF_SIZE_T__ 8\n"
|
||||
"\n"
|
||||
"#define __BYTE_ORDER__ 1234\n"
|
||||
"#define __ORDER_LITTLE_ENDIAN__ 1234\n"
|
||||
"#define __ORDER_BIG_ENDIAN__ 4321\n"
|
||||
"\n"
|
||||
"/* Some GCC predefined macros: */\n"
|
||||
"#define __SIZE_TYPE__ unsigned long\n"
|
||||
"#define __PTRDIFF_TYPE__ long\n"
|
||||
"#define __INTMAX_TYPE__ long\n"
|
||||
"#define __UINTMAX_TYPE__ unsigned long\n"
|
||||
"#define __INT8_TYPE__ signed char\n"
|
||||
"#define __INT16_TYPE__ short\n"
|
||||
"#define __INT32_TYPE__ int\n"
|
||||
"#define __INT64_TYPE__ long\n"
|
||||
"#define __UINT8_TYPE__ unsigned char\n"
|
||||
"#define __UINT16_TYPE__ unsigned short\n"
|
||||
"#define __UINT32_TYPE__ unsigned int\n"
|
||||
"#define __UINT64_TYPE__ unsigned long\n"
|
||||
"#define __INTPTR_TYPE__ long\n"
|
||||
"#define __UINTPTR_TYPE__ unsigned long\n"
|
||||
"\n"
|
||||
"#define __CHAR_BIT__ 8\n"
|
||||
"#define __INT8_MAX__ 127\n"
|
||||
"#define __INT16_MAX__ 32767\n"
|
||||
"#define __INT32_MAX__ 2147483647\n"
|
||||
"#define __INT64_MAX__ 9223372036854775807l\n"
|
||||
"#define __UINT8_MAX__ (__INT8_MAX__ * 2u + 1u)\n"
|
||||
"#define __UINT16_MAX__ (__INT16_MAX__ * 2u + 1u)\n"
|
||||
"#define __UINT32_MAX__ (__INT32_MAX__ * 2u + 1u)\n"
|
||||
"#define __UINT64_MAX__ (__INT64_MAX__ * 2u + 1u)\n"
|
||||
"#define __SCHAR_MAX__ __INT8_MAX__\n"
|
||||
"#define __SHRT_MAX__ __INT16_MAX__\n"
|
||||
"#define __INT_MAX__ __INT32_MAX__\n"
|
||||
"#define __LONG_MAX__ __INT64_MAX__\n"
|
||||
"#define __LONG_LONG_MAX__ __INT64_MAX__\n"
|
||||
"#define __SIZE_MAX__ __UINT64_MAX__\n"
|
||||
"#define __PTRDIFF_MAX__ __INT64_MAX__\n"
|
||||
"#define __INTMAX_MAX__ __INT64_MAX__\n"
|
||||
"#define __UINTMAX_MAX__ __UINT64_MAX__\n"
|
||||
"#define __INTPTR_MAX__ __INT64_MAX__\n"
|
||||
"#define __UINTPTR_MAX__ __UINT64_MAX__\n"
|
||||
"\n"
|
||||
"#define __FLT_MIN_EXP__ (-125)\n"
|
||||
"#define __FLT_MAX_EXP__ 128\n"
|
||||
"#define __FLT_DIG__ 6\n"
|
||||
"#define __FLT_DECIMAL_DIG__ 9\n"
|
||||
"#define __FLT_MANT_DIG__ 24\n"
|
||||
"#define __FLT_MIN__ 1.17549435082228750796873653722224568e-38F\n"
|
||||
"#define __FLT_MAX__ 3.40282346638528859811704183484516925e+38F\n"
|
||||
"#define __FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F\n"
|
||||
"\n"
|
||||
"#define __DBL_MIN_EXP__ (-1021)\n"
|
||||
"#define __DBL_MAX_EXP__ 1024\n"
|
||||
"#define __DBL_DIG__ 15\n"
|
||||
"#define __DBL_DECIMAL_DIG__ 17\n"
|
||||
"#define __DBL_MANT_DIG__ 53\n"
|
||||
"#define __DBL_MAX__ ((double) 1.79769313486231570814527423731704357e+308L)\n"
|
||||
"#define __DBL_MIN__ ((double) 2.22507385850720138309023271733240406e-308L)\n"
|
||||
"#define __DBL_EPSILON__ ((double) 2.22044604925031308084726333618164062e-16L)\n"
|
||||
"\n"
|
||||
"typedef unsigned short char16_t;\n"
|
||||
"typedef unsigned int char32_t;\n"
|
||||
"\n"
|
||||
#if defined(__linux__)
|
||||
"#define __gnu_linux__ 1\n"
|
||||
"#define __linux 1\n"
|
||||
"#define __linux__ 1\n"
|
||||
"#define linux 1\n"
|
||||
"#define __unix 1\n"
|
||||
"#define __unix__ 1\n"
|
||||
#endif
|
||||
"\n"
|
||||
"void *alloca (unsigned long);\n";
|
@ -0,0 +1,23 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
|
||||
#include "../mirc.h"
|
||||
#include "mirc-ppc64-linux.h"
|
||||
|
||||
static const char *standard_includes[] = {mirc, ppc64_mirc};
|
||||
|
||||
static const char *standard_include_dirs[] = {"include/mirc/", "include/mirc/ppc64/"};
|
||||
|
||||
#define MAX_ALIGNMENT 16
|
||||
|
||||
#define ADJUST_VAR_ALIGNMENT(c2m_ctx, align, type) \
|
||||
ppc64_adjust_var_alignment (c2m_ctx, align, type)
|
||||
|
||||
static int ppc64_adjust_var_alignment (c2m_ctx_t c2m_ctx, int align, struct type *type) {
|
||||
return align;
|
||||
}
|
||||
|
||||
static int invalid_alignment (mir_llong align) {
|
||||
return align != 0 && align != 1 && align != 2 && align != 4 && align != 8 && align != 16;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MIR_CHAR_BIT 8
|
||||
|
||||
typedef int8_t mir_schar;
|
||||
typedef int16_t mir_short;
|
||||
typedef int32_t mir_int;
|
||||
typedef int64_t mir_long;
|
||||
typedef int64_t mir_llong;
|
||||
|
||||
#define MIR_SCHAR_MIN INT8_MIN
|
||||
#define MIR_SCHAR_MAX INT8_MAX
|
||||
#define MIR_SHORT_MIN INT16_MIN
|
||||
#define MIR_SHORT_MAX INT16_MAX
|
||||
#define MIR_INT_MIN INT32_MIN
|
||||
#define MIR_INT_MAX INT32_MAX
|
||||
#define MIR_LONG_MIN INT64_MIN
|
||||
#define MIR_LONG_MAX INT64_MAX
|
||||
#define MIR_LLONG_MIN INT64_MIN
|
||||
#define MIR_LLONG_MAX INT64_MAX
|
||||
|
||||
typedef uint8_t mir_uchar;
|
||||
typedef uint16_t mir_ushort;
|
||||
typedef uint32_t mir_uint;
|
||||
typedef uint64_t mir_ulong;
|
||||
typedef uint64_t mir_ullong;
|
||||
|
||||
#define MIR_UCHAR_MAX UINT8_MAX
|
||||
#define MIR_USHORT_MAX UINT16_MAX
|
||||
#define MIR_UINT_MAX UINT32_MAX
|
||||
#define MIR_ULONG_MAX UINT64_MAX
|
||||
#define MIR_ULLONG_MAX UINT64_MAX
|
||||
|
||||
typedef mir_schar mir_char;
|
||||
#define MIR_CHAR_MIN MIR_SCHAR_MIN
|
||||
#define MIR_CHAR_MAX MIR_SCHAR_MAX
|
||||
|
||||
typedef float mir_float;
|
||||
typedef double mir_double;
|
||||
typedef long double mir_ldouble;
|
||||
|
||||
typedef uint8_t mir_bool;
|
||||
typedef int64_t mir_ptrdiff_t;
|
||||
typedef uint64_t mir_size_t;
|
||||
|
||||
#define MIR_SIZE_MAX UINT64_MAX
|
@ -0,0 +1,95 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
|
||||
static char ppc64_mirc[]
|
||||
= "#define __PPC64__ 1\n"
|
||||
"#define _ARCH_PPC64 1\n"
|
||||
"#define _LP64 1\n"
|
||||
"#define __LP64__ 1\n"
|
||||
"\n"
|
||||
"#define __LONG_DOUBLE_128__ 1\n" // ???
|
||||
"#define __SIZEOF_DOUBLE__ 8\n"
|
||||
"#define __SIZEOF_FLOAT__ 4\n"
|
||||
"#define __SIZEOF_INT__ 4\n"
|
||||
"#define __SIZEOF_LONG_DOUBLE__ 16\n"
|
||||
"#define __SIZEOF_LONG_LONG__ 8\n"
|
||||
"#define __SIZEOF_LONG__ 8\n"
|
||||
"#define __SIZEOF_POINTER__ 8\n"
|
||||
"#define __SIZEOF_PTRDIFF_T__ 8\n"
|
||||
"#define __SIZEOF_SHORT__ 2\n"
|
||||
"#define __SIZEOF_SIZE_T__ 8\n"
|
||||
"\n"
|
||||
"#define _BIG_ENDIAN 1\n" // ??? Implement LE too
|
||||
"#define __ORDER_LITTLE_ENDIAN__ 1234\n"
|
||||
"#define __ORDER_BIG_ENDIAN__ 4321\n"
|
||||
"#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__\n"
|
||||
"\n"
|
||||
"/* Some GCC predefined macros: */\n"
|
||||
"#define __SIZE_TYPE__ unsigned long\n"
|
||||
"#define __PTRDIFF_TYPE__ long\n"
|
||||
"#define __INTMAX_TYPE__ long\n"
|
||||
"#define __UINTMAX_TYPE__ unsigned long\n"
|
||||
"#define __INT8_TYPE__ signed char\n"
|
||||
"#define __INT16_TYPE__ short\n"
|
||||
"#define __INT32_TYPE__ int\n"
|
||||
"#define __INT64_TYPE__ long\n"
|
||||
"#define __UINT8_TYPE__ unsigned char\n"
|
||||
"#define __UINT16_TYPE__ unsigned short\n"
|
||||
"#define __UINT32_TYPE__ unsigned int\n"
|
||||
"#define __UINT64_TYPE__ unsigned long\n"
|
||||
"#define __INTPTR_TYPE__ long\n"
|
||||
"#define __UINTPTR_TYPE__ unsigned long\n"
|
||||
"\n"
|
||||
"#define __CHAR_BIT__ 8\n"
|
||||
"#define __INT8_MAX__ 127\n"
|
||||
"#define __INT16_MAX__ 32767\n"
|
||||
"#define __INT32_MAX__ 2147483647\n"
|
||||
"#define __INT64_MAX__ 9223372036854775807l\n"
|
||||
"#define __UINT8_MAX__ (__INT8_MAX__ * 2u + 1u)\n"
|
||||
"#define __UINT16_MAX__ (__INT16_MAX__ * 2u + 1u)\n"
|
||||
"#define __UINT32_MAX__ (__INT32_MAX__ * 2u + 1u)\n"
|
||||
"#define __UINT64_MAX__ (__INT64_MAX__ * 2u + 1u)\n"
|
||||
"#define __SCHAR_MAX__ __INT8_MAX__\n"
|
||||
"#define __SHRT_MAX__ __INT16_MAX__\n"
|
||||
"#define __INT_MAX__ __INT32_MAX__\n"
|
||||
"#define __LONG_MAX__ __INT64_MAX__\n"
|
||||
"#define __LONG_LONG_MAX__ __INT64_MAX__\n"
|
||||
"#define __SIZE_MAX__ __UINT64_MAX__\n"
|
||||
"#define __PTRDIFF_MAX__ __INT64_MAX__\n"
|
||||
"#define __INTMAX_MAX__ __INT64_MAX__\n"
|
||||
"#define __UINTMAX_MAX__ __UINT64_MAX__\n"
|
||||
"#define __INTPTR_MAX__ __INT64_MAX__\n"
|
||||
"#define __UINTPTR_MAX__ __UINT64_MAX__\n"
|
||||
"\n"
|
||||
"#define __FLT_MIN_EXP__ (-125)\n"
|
||||
"#define __FLT_MAX_EXP__ 128\n"
|
||||
"#define __FLT_DIG__ 6\n"
|
||||
"#define __FLT_DECIMAL_DIG__ 9\n"
|
||||
"#define __FLT_MANT_DIG__ 24\n"
|
||||
"#define __FLT_MIN__ 1.17549435082228750796873653722224568e-38F\n"
|
||||
"#define __FLT_MAX__ 3.40282346638528859811704183484516925e+38F\n"
|
||||
"#define __FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F\n"
|
||||
"\n"
|
||||
"#define __DBL_MIN_EXP__ (-1021)\n"
|
||||
"#define __DBL_MAX_EXP__ 1024\n"
|
||||
"#define __DBL_DIG__ 15\n"
|
||||
"#define __DBL_DECIMAL_DIG__ 17\n"
|
||||
"#define __DBL_MANT_DIG__ 53\n"
|
||||
"#define __DBL_MAX__ ((double) 1.79769313486231570814527423731704357e+308L)\n"
|
||||
"#define __DBL_MIN__ ((double) 2.22507385850720138309023271733240406e-308L)\n"
|
||||
"#define __DBL_EPSILON__ ((double) 2.22044604925031308084726333618164062e-16L)\n"
|
||||
"\n"
|
||||
"typedef unsigned short char16_t;\n"
|
||||
"typedef unsigned int char32_t;\n"
|
||||
"\n"
|
||||
#if defined(__linux__)
|
||||
"#define __gnu_linux__ 1\n"
|
||||
"#define __linux 1\n"
|
||||
"#define __linux__ 1\n"
|
||||
"#define linux 1\n"
|
||||
"#define __unix 1\n"
|
||||
"#define __unix__ 1\n"
|
||||
#endif
|
||||
"\n"
|
||||
"void *alloca (unsigned long);\n";
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
||||
#include "mir-gen-stub.c"
|
@ -0,0 +1,95 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2018-2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
|
||||
Stub for MIR generator machine dependent file. It contains
|
||||
definitions used by MIR generator. You can use this file for
|
||||
successful compilation of mir-gen.c.
|
||||
|
||||
See HOW-TO-PORT-MIR.md document for the definitions description.
|
||||
*/
|
||||
|
||||
enum {
|
||||
R0_HARD_REG,
|
||||
R1_HARD_REG,
|
||||
R2_HARD_REG,
|
||||
R3_HARD_REG,
|
||||
R4_HARD_REG,
|
||||
R5_HARD_REG,
|
||||
R6_HARD_REG,
|
||||
R7_HARD_REG,
|
||||
F0_HARD_REG,
|
||||
F1_HARD_REG,
|
||||
F2_HARD_REG,
|
||||
F3_HARD_REG,
|
||||
F4_HARD_REG,
|
||||
F5_HARD_REG,
|
||||
F6_HARD_REG,
|
||||
F7_HARD_REG
|
||||
};
|
||||
|
||||
static const MIR_reg_t MAX_HARD_REG = F7_HARD_REG; /* max value for the previous regs */
|
||||
static const MIR_reg_t FP_HARD_REG = R6_HARD_REG; /* stack frame pointer according ABI */
|
||||
static const MIR_reg_t SP_HARD_REG = R7_HARD_REG; /* stack pointer according ABI */
|
||||
|
||||
const MIR_reg_t TEMP_INT_HARD_REG1 = R2_HARD_REG, TEMP_INT_HARD_REG2 = R3_HARD_REG;
|
||||
const MIR_reg_t TEMP_FLOAT_HARD_REG1 = F2_HARD_REG, TEMP_FLOAT_HARD_REG2 = F3_HARD_REG;
|
||||
const MIR_reg_t TEMP_DOUBLE_HARD_REG1 = F2_HARD_REG, TEMP_DOUBLE_HARD_REG2 = F3_HARD_REG;
|
||||
const MIR_reg_t TEMP_LDOUBLE_HARD_REG1 = F2_HARD_REG;
|
||||
const MIR_reg_t TEMP_LDOUBLE_HARD_REG2 = F3_HARD_REG;
|
||||
|
||||
static int target_locs_num (MIR_reg_t loc, MIR_type_t type) {
|
||||
return loc > MAX_HARD_REG && type == MIR_T_LD ? 2 : 1;
|
||||
}
|
||||
|
||||
static inline int target_hard_reg_type_ok_p (MIR_reg_t hard_reg, MIR_type_t type) {
|
||||
assert (hard_reg <= MAX_HARD_REG);
|
||||
return (type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? hard_reg >= F0_HARD_REG
|
||||
: hard_reg < F0_HARD_REG);
|
||||
}
|
||||
|
||||
static inline int target_fixed_hard_reg_p (MIR_reg_t hard_reg) {
|
||||
assert (hard_reg <= MAX_HARD_REG);
|
||||
return (hard_reg == FP_HARD_REG || hard_reg == SP_HARD_REG
|
||||
|| hard_reg == TEMP_INT_HARD_REG1 || hard_reg == TEMP_INT_HARD_REG2
|
||||
|| hard_reg == TEMP_FLOAT_HARD_REG1 || hard_reg == TEMP_FLOAT_HARD_REG2
|
||||
|| hard_reg == TEMP_DOUBLE_HARD_REG1 || hard_reg == TEMP_DOUBLE_HARD_REG2
|
||||
|| hard_reg == TEMP_LDOUBLE_HARD_REG1 || hard_reg == TEMP_LDOUBLE_HARD_REG2);
|
||||
}
|
||||
|
||||
static inline int target_call_used_hard_reg_p (MIR_reg_t hard_reg) {
|
||||
assert (hard_reg <= MAX_HARD_REG);
|
||||
return !((hard_reg >= R4_HARD_REG && hard_reg <= R5_HARD_REG)
|
||||
|| (hard_reg >= F2_HARD_REG && hard_reg <= F7_HARD_REG));
|
||||
}
|
||||
|
||||
static const int slots_offset = 176; /* It is used in this file but not in MIR generator */
|
||||
|
||||
static MIR_disp_t target_get_stack_slot_offset (MIR_context_t ctx, MIR_type_t type,
|
||||
MIR_reg_t slot) {
|
||||
/* slot is 0, 1, ... */
|
||||
struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx);
|
||||
|
||||
return -((MIR_disp_t) (slot + (type == MIR_T_LD ? 2 : 1)) * 8 + slots_offset);
|
||||
}
|
||||
|
||||
static const MIR_insn_code_t target_io_dup_op_insn_codes[] = {MIR_INSN_BOUND};
|
||||
|
||||
static void target_machinize (MIR_context_t ctx) {}
|
||||
|
||||
static void target_make_prolog_epilog (MIR_context_t ctx, bitmap_t used_hard_regs,
|
||||
size_t stack_slots_num) {}
|
||||
|
||||
static void target_get_early_clobbered_hard_regs (MIR_insn_t insn, MIR_reg_t *hr1, MIR_reg_t *hr2) {
|
||||
*hr1 = *hr2 = MIR_NON_HARD_REG;
|
||||
}
|
||||
|
||||
static int target_insn_ok_p (MIR_context_t ctx, MIR_insn_t insn) { return FALSE; }
|
||||
static uint8_t *target_translate (MIR_context_t ctx, size_t *len) { return NULL; }
|
||||
static void target_rebase (MIR_context_t ctx, uint8_t *base) {}
|
||||
|
||||
static void target_init (MIR_context_t ctx) {
|
||||
fprintf (stderr, "Your generator target dependent file is just a stub!\n");
|
||||
fprintf (stderr, "MIR generator can not use it -- good bye.\n");
|
||||
exit (1);
|
||||
}
|
||||
static void target_finish (MIR_context_t ctx) {}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,467 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2018-2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
|
||||
// _MIR_get_thunk, _MIR_redirect_thunk, _MIR_get_interp_shim, _MIR_get_ff_call, _MIR_get_wrapper
|
||||
#define VA_LIST_IS_ARRAY_P 1 /* one element which is a pointer to args */
|
||||
|
||||
#define FUNC_DESC_LEN 24
|
||||
static void ppc64_push_func_desc (MIR_context_t ctx);
|
||||
void (*ppc64_func_desc) (MIR_context_t ctx) = ppc64_push_func_desc;
|
||||
|
||||
static void ppc64_push_func_desc (MIR_context_t ctx) {
|
||||
VARR_TRUNC (uint8_t, machine_insns, 0);
|
||||
for (int i = 0; i < FUNC_DESC_LEN; i++)
|
||||
VARR_PUSH (uint8_t, machine_insns, ((uint8_t *) ppc64_func_desc)[i]);
|
||||
}
|
||||
|
||||
static void ppc64_redirect_func_desc (MIR_context_t ctx, void *desc, void *to) {
|
||||
mir_assert (((uint64_t) desc & 0x3) == 0 && ((uint64_t) to & 0x3) == 0); /* alignment */
|
||||
_MIR_change_code (ctx, desc, (uint8_t *) &to, sizeof (to));
|
||||
}
|
||||
|
||||
static void *ppc64_publish_func_and_redirect (MIR_context_t ctx) {
|
||||
void *res = _MIR_publish_code (ctx, VARR_ADDR (uint8_t, machine_insns),
|
||||
VARR_LENGTH (uint8_t, machine_insns));
|
||||
ppc64_redirect_func_desc (ctx, res, (uint8_t *) res + FUNC_DESC_LEN);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void push_insn (MIR_context_t ctx, uint32_t insn) {
|
||||
uint8_t *p = (uint8_t *) &insn;
|
||||
for (size_t i = 0; i < 4; i++) VARR_PUSH (uint8_t, machine_insns, p[i]);
|
||||
}
|
||||
|
||||
static void push_insns (MIR_context_t ctx, const uint32_t *pat, size_t pat_len) {
|
||||
uint8_t *p = (uint8_t *) pat;
|
||||
for (size_t i = 0; i < pat_len; i++) VARR_PUSH (uint8_t, machine_insns, p[i]);
|
||||
}
|
||||
|
||||
void *_MIR_get_bstart_builtin (MIR_context_t ctx) {
|
||||
static const uint32_t bstart_code[] = {
|
||||
0x7c230b78, /* mr 3,1 */
|
||||
0x4e800020, /* blr */
|
||||
};
|
||||
ppc64_push_func_desc (ctx);
|
||||
push_insns (ctx, bstart_code, sizeof (bstart_code));
|
||||
return ppc64_publish_func_and_redirect (ctx);
|
||||
}
|
||||
|
||||
void *_MIR_get_bend_builtin (MIR_context_t ctx) {
|
||||
static const uint32_t bend_code[] = {
|
||||
0xe8010000, /* ld r0,0(r1) */
|
||||
0xf8030000, /* std r0,0(r3) */
|
||||
0xe8010028, /* ld r0,40(r1) */
|
||||
0xf8030028, /* std r0,40(r3) */
|
||||
0x7c611b78, /* mr r1,r3 */
|
||||
0x4e800020, /* blr */
|
||||
};
|
||||
ppc64_push_func_desc (ctx);
|
||||
push_insns (ctx, bend_code, sizeof (bend_code));
|
||||
return ppc64_publish_func_and_redirect (ctx);
|
||||
}
|
||||
|
||||
void *_MIR_get_thunk (MIR_context_t ctx) { /* emit 3 doublewords for func descriptor: */
|
||||
ppc64_push_func_desc (ctx);
|
||||
return ppc64_publish_func_and_redirect (ctx);
|
||||
}
|
||||
|
||||
void _MIR_redirect_thunk (MIR_context_t ctx, void *thunk, void *to) {
|
||||
ppc64_redirect_func_desc (ctx, thunk, to);
|
||||
}
|
||||
|
||||
struct ppc64_va_list {
|
||||
uint64_t *arg_area;
|
||||
};
|
||||
|
||||
void *va_arg_builtin (void *p, uint64_t t) {
|
||||
struct ppc64_va_list *va = p;
|
||||
MIR_type_t type = t;
|
||||
int fp_p = type == MIR_T_F || type == MIR_T_D;
|
||||
void *a = va->arg_area;
|
||||
|
||||
if (type == MIR_T_F || type == MIR_T_I32) {
|
||||
a = (char *) a + 4; /* 2nd word of doubleword */
|
||||
va->arg_area = (uint64_t *) ((char *) a + 4);
|
||||
} else if (type == MIR_T_LD) {
|
||||
va->arg_area += 2;
|
||||
} else {
|
||||
va->arg_area++;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void va_start_interp_builtin (MIR_context_t ctx, void *p, void *a) {
|
||||
struct ppc64_va_list **va = p;
|
||||
va_list *vap = a;
|
||||
|
||||
assert (sizeof (struct ppc64_va_list) == sizeof (va_list));
|
||||
*va = (struct ppc64_va_list *) vap;
|
||||
}
|
||||
|
||||
void va_end_interp_builtin (MIR_context_t ctx, void *p) {}
|
||||
|
||||
static void ppc64_gen_mov (MIR_context_t ctx, unsigned to, unsigned from) {
|
||||
/* or to,from,from: */
|
||||
push_insn (ctx, (31 << 26) | (444 << 1) | (from << 21) | (to << 16) | (from << 11));
|
||||
}
|
||||
|
||||
static void ppc64_gen_addi (MIR_context_t ctx, unsigned rt_reg, unsigned ra_reg, int disp) {
|
||||
push_insn (ctx, (14 << 26) | (rt_reg << 21) | (ra_reg << 16) | (disp & 0xffff));
|
||||
}
|
||||
|
||||
static void ppc64_gen_ld (MIR_context_t ctx, unsigned to, unsigned base, int disp,
|
||||
MIR_type_t type) {
|
||||
int single_p = type == MIR_T_F;
|
||||
int double_p = type == MIR_T_D || type == MIR_T_LD;
|
||||
/* (ld | lf[sd]) to, disp(base): */
|
||||
assert (base != 0 && base < 32 && to < 32 && (single_p || double_p || (disp & 0x3) == 0));
|
||||
push_insn (ctx, ((single_p ? 48 : double_p ? 50 : 58) << 26) | (to << 21) | (base << 16)
|
||||
| (disp & 0xffff));
|
||||
}
|
||||
|
||||
static void ppc64_gen_st (MIR_context_t ctx, unsigned from, unsigned base, int disp,
|
||||
MIR_type_t type) {
|
||||
int single_p = type == MIR_T_F;
|
||||
int double_p = type == MIR_T_D || type == MIR_T_LD;
|
||||
/* std|stf[sd] from, disp(base): */
|
||||
assert (base != 0 && base < 32 && from < 32 && (single_p || double_p || (disp & 0x3) == 0));
|
||||
push_insn (ctx, ((single_p ? 52 : double_p ? 54 : 62) << 26) | (from << 21) | (base << 16)
|
||||
| (disp & 0xffff));
|
||||
}
|
||||
|
||||
static void ppc64_gen_stdu (MIR_context_t ctx, int disp) {
|
||||
assert ((disp & 0x3) == 0);
|
||||
push_insn (ctx, 0xf8210001 | disp & 0xfffc); /* stdu 1, disp (1) */
|
||||
}
|
||||
|
||||
static void ppc64_gen_address (MIR_context_t ctx, unsigned int reg, void *p) {
|
||||
uint64_t a = (uint64_t) p;
|
||||
if ((a >> 32) == 0) {
|
||||
if (((a >> 31) & 1) == 0) { /* lis r,0,Z2 */
|
||||
push_insn (ctx, (15 << 26) | (reg << 21) | (0 << 16) | (a >> 16) & 0xffff);
|
||||
} else { /* xor r,r,r; oris r,r,Z2 */
|
||||
push_insn (ctx, (31 << 26) | (316 << 1) | (reg << 21) | (reg << 16) | (reg << 11));
|
||||
push_insn (ctx, (25 << 26) | (reg << 21) | (reg << 16) | (a >> 16) & 0xffff);
|
||||
}
|
||||
} else {
|
||||
/* lis r,0,Z0; ori r,r,Z1; rldicr r,r,32,31; oris r,r,Z2; ori r,r,Z3: */
|
||||
push_insn (ctx, (15 << 26) | (reg << 21) | (0 << 16) | (a >> 48));
|
||||
push_insn (ctx, (24 << 26) | (reg << 21) | (reg << 16) | (a >> 32) & 0xffff);
|
||||
push_insn (ctx, (30 << 26) | (reg << 21) | (reg << 16) | 0x07c6);
|
||||
push_insn (ctx, (25 << 26) | (reg << 21) | (reg << 16) | (a >> 16) & 0xffff);
|
||||
}
|
||||
push_insn (ctx, (24 << 26) | (reg << 21) | (reg << 16) | a & 0xffff);
|
||||
}
|
||||
|
||||
static void ppc64_gen_jump (MIR_context_t ctx, unsigned int reg, int call_p) {
|
||||
ppc64_gen_ld (ctx, 0, reg, 0, MIR_T_I64); /* 0 = func addr */
|
||||
ppc64_gen_ld (ctx, 2, reg, 8, MIR_T_I64); /* r2 = TOC */
|
||||
push_insn (ctx, (31 << 26) | (467 << 1) | (0 << 21) | (9 << 16)); /* mctr 0 */
|
||||
push_insn (ctx, (19 << 26) | (528 << 1) | (20 << 21) | (call_p ? 1 : 0)); /* bcctr[l] */
|
||||
}
|
||||
|
||||
/* Generation: fun (fun_addr, res_arg_addresses):
|
||||
save lr (r1 + 16); allocate and form minimal stack frame (with necessary param area); save r14;
|
||||
r12=fun_addr (r3); r14 = res_arg_addresses (r4);
|
||||
r0=mem[r14,<args_offset>]; (arg_reg=mem[r0] or r0=mem[r0];mem[r1,r1_offset]=r0) ...
|
||||
if func is vararg: put fp args also in gp regs
|
||||
call *r12;
|
||||
r0=mem[r14,<offset>]; res_reg=mem[r0]; ...
|
||||
restore r14, r1, lr; return. */
|
||||
void *_MIR_get_ff_call (MIR_context_t ctx, size_t nres, MIR_type_t *res_types, size_t nargs,
|
||||
MIR_type_t *arg_types, int vararg_p) {
|
||||
static uint32_t start_pattern[] = {
|
||||
0x7c0802a6, /* mflr r0 */
|
||||
0xf8010010, /* std r0,16(r1) */
|
||||
};
|
||||
static uint32_t finish_pattern[] = {
|
||||
0xe8010010, /* ld r0,16(r1) */
|
||||
0x7c0803a6, /* mtlr r0 */
|
||||
0x4e800020, /* blr */
|
||||
};
|
||||
MIR_type_t type;
|
||||
int n_gpregs = 0, n_fpregs = 0, res_reg = 14, frame_size, disp, param_offset, param_size = 0;
|
||||
|
||||
ppc64_push_func_desc (ctx);
|
||||
for (uint32_t i = 0; i < nargs; i++) param_size += arg_types[i] == MIR_T_LD ? 16 : 8;
|
||||
if (param_size < 64) param_size = 64;
|
||||
frame_size = 48 + param_size + 8; /* +local var to save res_reg */
|
||||
if (frame_size % 8 != 0) frame_size += 8; /* align */
|
||||
ppc64_gen_st (ctx, 2, 1, 40, MIR_T_I64);
|
||||
push_insns (ctx, start_pattern, sizeof (start_pattern));
|
||||
ppc64_gen_stdu (ctx, -frame_size);
|
||||
ppc64_gen_st (ctx, res_reg, 1, 48 + param_size, MIR_T_I64); /* save res_reg */
|
||||
mir_assert (sizeof (long double) == 16);
|
||||
ppc64_gen_mov (ctx, res_reg, 4); /* results & args */
|
||||
ppc64_gen_mov (ctx, 12, 3); /* func addr */
|
||||
n_gpregs = n_fpregs = 0;
|
||||
param_offset = nres * 16; /* args start */
|
||||
disp = 48; /* param area start */
|
||||
for (uint32_t i = 0; i < nargs; i++) { /* load args: */
|
||||
type = arg_types[i];
|
||||
if ((type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) && n_fpregs < 13) {
|
||||
ppc64_gen_ld (ctx, 1 + n_fpregs, res_reg, param_offset, type);
|
||||
if (vararg_p) {
|
||||
if (n_gpregs >= 8) {
|
||||
ppc64_gen_st (ctx, 1 + n_fpregs, 1, disp, MIR_T_D);
|
||||
} else { /* load gp reg to */
|
||||
ppc64_gen_st (ctx, 1 + n_fpregs, 1, -8, MIR_T_D);
|
||||
ppc64_gen_ld (ctx, 3 + n_gpregs, 1, -8, MIR_T_I64);
|
||||
}
|
||||
}
|
||||
n_fpregs++;
|
||||
if (type == MIR_T_LD) {
|
||||
if (n_fpregs < 13) {
|
||||
ppc64_gen_ld (ctx, 1 + n_fpregs, res_reg, param_offset + 8, type);
|
||||
if (vararg_p) {
|
||||
if (n_gpregs + 1 >= 8) {
|
||||
ppc64_gen_st (ctx, 1 + n_fpregs, 1, disp + 8, MIR_T_D);
|
||||
} else { /* load gp reg to */
|
||||
ppc64_gen_st (ctx, 1 + n_fpregs, 1, -8, MIR_T_D);
|
||||
ppc64_gen_ld (ctx, 4 + n_gpregs, 1, -8, MIR_T_I64);
|
||||
}
|
||||
}
|
||||
n_fpregs++;
|
||||
} else {
|
||||
ppc64_gen_ld (ctx, 0, res_reg, param_offset + 8, type);
|
||||
ppc64_gen_st (ctx, 0, 1, disp + 8, MIR_T_D);
|
||||
}
|
||||
}
|
||||
} else if (n_gpregs < 8) {
|
||||
ppc64_gen_ld (ctx, n_gpregs + 3, res_reg, param_offset, MIR_T_I64);
|
||||
} else if (type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) {
|
||||
ppc64_gen_ld (ctx, 0, res_reg, param_offset, type);
|
||||
ppc64_gen_st (ctx, 0, 1, disp, MIR_T_D);
|
||||
if (type == MIR_T_LD) {
|
||||
ppc64_gen_ld (ctx, 0, res_reg, param_offset + 8, type);
|
||||
ppc64_gen_st (ctx, 0, 1, disp + 8, MIR_T_D);
|
||||
}
|
||||
} else {
|
||||
ppc64_gen_ld (ctx, 0, res_reg, param_offset, MIR_T_I64);
|
||||
ppc64_gen_st (ctx, 0, 1, disp, MIR_T_I64);
|
||||
}
|
||||
disp += type == MIR_T_LD ? 16 : 8;
|
||||
param_offset += 16;
|
||||
n_gpregs += type == MIR_T_LD ? 2 : 1;
|
||||
}
|
||||
ppc64_gen_jump (ctx, 12, TRUE); /* call func_addr */
|
||||
n_gpregs = n_fpregs = 0;
|
||||
disp = 0;
|
||||
for (uint32_t i = 0; i < nres; i++) {
|
||||
type = res_types[i];
|
||||
if ((type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) && n_fpregs < 4) {
|
||||
ppc64_gen_st (ctx, n_fpregs + 1, res_reg, disp, type);
|
||||
n_fpregs++;
|
||||
if (type == MIR_T_LD) {
|
||||
if (n_fpregs >= 4)
|
||||
(*error_func) (MIR_ret_error, "ppc64 can not handle this combination of return values");
|
||||
ppc64_gen_st (ctx, n_fpregs + 1, res_reg, disp + 8, type);
|
||||
n_fpregs++;
|
||||
}
|
||||
} else if (n_gpregs < 1) { // just one gp reg
|
||||
ppc64_gen_st (ctx, n_gpregs + 3, res_reg, disp, MIR_T_I64);
|
||||
n_gpregs++;
|
||||
} else {
|
||||
(*error_func) (MIR_ret_error, "ppc64 can not handle this combination of return values");
|
||||
}
|
||||
disp += 16;
|
||||
}
|
||||
ppc64_gen_ld (ctx, res_reg, 1, 48 + param_size, MIR_T_I64); /* restore res_reg */
|
||||
ppc64_gen_addi (ctx, 1, 1, frame_size);
|
||||
push_insns (ctx, finish_pattern, sizeof (finish_pattern));
|
||||
return ppc64_publish_func_and_redirect (ctx);
|
||||
}
|
||||
|
||||
/* Transform C call to call of void handler (MIR_context_t ctx, MIR_item_t func_item,
|
||||
va_list va, MIR_val_t *results):
|
||||
Brief: put all C call args to local vars (or if va_arg do nothing); save lr (r1+16), r14;
|
||||
allocate and form minimal shim stack frame (param area = 8 * 8);
|
||||
call handler with args; move results(r14) to return regs; restore lr,r14,r1; return */
|
||||
void *_MIR_get_interp_shim (MIR_context_t ctx, MIR_item_t func_item, void *handler) {
|
||||
MIR_func_t func = func_item->u.func;
|
||||
uint32_t nres = func->nres, nargs = func->nargs;
|
||||
int vararg_p = func->vararg_p;
|
||||
MIR_type_t type, *res_types = func->res_types;
|
||||
MIR_var_t *arg_vars = VARR_ADDR (MIR_var_t, func->vars);
|
||||
int disp, size, frame_size, local_var_size, param_offset, va_reg = 11, caller_r1 = 12,
|
||||
res_reg = 14;
|
||||
int n_gpregs, n_fpregs;
|
||||
static uint32_t start_pattern[] = {
|
||||
0x7c0802a6, /* mflr r0 */
|
||||
0xf8010010, /* std r0,16(r1) */
|
||||
};
|
||||
static uint32_t finish_pattern[] = {
|
||||
0xe8010010, /* ld r0,16(r1) */
|
||||
0x7c0803a6, /* mtlr r0 */
|
||||
0x4e800020, /* blr */
|
||||
};
|
||||
static uint32_t save_gp_regs_pattern[] = {
|
||||
0xf8610030, /* std r3,48(r1) */
|
||||
0xf8810038, /* std r4,56(r1) */
|
||||
0xf8a10040, /* std r5,64(r1) */
|
||||
0xf8c10048, /* std r6,72(r1) */
|
||||
0xf8e10050, /* std r7,80(r1) */
|
||||
0xf9010058, /* std r8,88(r1) */
|
||||
0xf9210060, /* std r9,96(r1) */
|
||||
0xf9410068, /* std r10,104(r1) */
|
||||
};
|
||||
|
||||
VARR_TRUNC (uint8_t, machine_insns, 0);
|
||||
frame_size = 112; /* 6(frame start) + 8(param area) */
|
||||
local_var_size = nres * 16 + 8; /* saved r14, results */
|
||||
if (vararg_p) {
|
||||
push_insns (ctx, save_gp_regs_pattern, sizeof (save_gp_regs_pattern));
|
||||
ppc64_gen_addi (ctx, va_reg, 1, 48);
|
||||
} else {
|
||||
ppc64_gen_mov (ctx, caller_r1, 1); /* caller frame r1 */
|
||||
for (uint32_t i = 0; i < nargs; i++) {
|
||||
type = arg_vars[i].type;
|
||||
local_var_size += type == MIR_T_LD ? 16 : 8;
|
||||
}
|
||||
}
|
||||
frame_size += local_var_size;
|
||||
if (frame_size % 8 != 0) frame_size += 8; /* align */
|
||||
push_insns (ctx, start_pattern, sizeof (start_pattern));
|
||||
ppc64_gen_stdu (ctx, -frame_size);
|
||||
ppc64_gen_st (ctx, res_reg, 1, 48 + 64, MIR_T_I64); /* save res_reg */
|
||||
if (!vararg_p) { /* save args in local vars: */
|
||||
disp = 112 + nres * 16 + 8; /* 48 + 64 + nres * 16 + 8: start of local vars to keep args */
|
||||
ppc64_gen_addi (ctx, va_reg, 1, disp);
|
||||
param_offset = 48;
|
||||
n_gpregs = n_fpregs = 0;
|
||||
for (uint32_t i = 0; i < nargs; i++) {
|
||||
type = arg_vars[i].type;
|
||||
if ((type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) && n_fpregs < 13) {
|
||||
ppc64_gen_st (ctx, n_fpregs + 1, 1, disp, MIR_T_D);
|
||||
n_fpregs++;
|
||||
if (type == MIR_T_LD) {
|
||||
if (n_fpregs < 13) {
|
||||
ppc64_gen_st (ctx, n_fpregs + 1, 1, disp + 8, MIR_T_D);
|
||||
n_fpregs++;
|
||||
} else {
|
||||
ppc64_gen_ld (ctx, 0, caller_r1, param_offset + 8, MIR_T_D);
|
||||
ppc64_gen_st (ctx, 0, 1, disp + 8, MIR_T_D);
|
||||
}
|
||||
}
|
||||
} else if (n_gpregs < 8) {
|
||||
ppc64_gen_st (ctx, n_gpregs + 3, 1, disp, MIR_T_I64);
|
||||
} else if (type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) {
|
||||
ppc64_gen_ld (ctx, 0, caller_r1, param_offset + (type == MIR_T_F ? 4 : 0), type);
|
||||
ppc64_gen_st (ctx, 0, 1, disp, MIR_T_D);
|
||||
if (type == MIR_T_LD) {
|
||||
ppc64_gen_ld (ctx, 0, caller_r1, param_offset + 8, MIR_T_D);
|
||||
ppc64_gen_st (ctx, 0, 1, disp + 8, MIR_T_D);
|
||||
}
|
||||
} else {
|
||||
ppc64_gen_ld (ctx, 0, caller_r1, param_offset, MIR_T_I64);
|
||||
ppc64_gen_st (ctx, 0, 1, disp, MIR_T_I64);
|
||||
}
|
||||
size = type == MIR_T_LD ? 16 : 8;
|
||||
disp += size;
|
||||
param_offset += size;
|
||||
n_gpregs += type == MIR_T_LD ? 2 : 1;
|
||||
}
|
||||
}
|
||||
ppc64_gen_addi (ctx, res_reg, 1, 64 + 48 + 8);
|
||||
ppc64_gen_address (ctx, 3, ctx);
|
||||
ppc64_gen_address (ctx, 4, func_item);
|
||||
ppc64_gen_mov (ctx, 5, va_reg);
|
||||
ppc64_gen_mov (ctx, 6, res_reg);
|
||||
ppc64_gen_address (ctx, 7, handler);
|
||||
ppc64_gen_jump (ctx, 7, TRUE);
|
||||
disp = n_gpregs = n_fpregs = 0;
|
||||
for (uint32_t i = 0; i < nres; i++) {
|
||||
type = res_types[i];
|
||||
if ((type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) && n_fpregs < 4) {
|
||||
ppc64_gen_ld (ctx, n_fpregs + 1, res_reg, disp, type);
|
||||
n_fpregs++;
|
||||
if (type == MIR_T_LD) {
|
||||
if (n_fpregs >= 4)
|
||||
(*error_func) (MIR_ret_error, "ppc64 can not handle this combination of return values");
|
||||
ppc64_gen_ld (ctx, n_fpregs + 1, res_reg, disp + 8, type);
|
||||
n_fpregs++;
|
||||
}
|
||||
} else if (n_gpregs < 1) { // just one gp reg
|
||||
ppc64_gen_ld (ctx, n_gpregs + 3, res_reg, disp, MIR_T_I64);
|
||||
n_gpregs++;
|
||||
} else {
|
||||
(*error_func) (MIR_ret_error, "ppc64 can not handle this combination of return values");
|
||||
}
|
||||
disp += 16;
|
||||
}
|
||||
ppc64_gen_ld (ctx, res_reg, 1, 48 + 64, MIR_T_I64); /* restore res_reg */
|
||||
ppc64_gen_addi (ctx, 1, 1, frame_size);
|
||||
push_insns (ctx, finish_pattern, sizeof (finish_pattern));
|
||||
return _MIR_publish_code (ctx, VARR_ADDR (uint8_t, machine_insns),
|
||||
VARR_LENGTH (uint8_t, machine_insns));
|
||||
}
|
||||
|
||||
/* Brief: save lr (r1+16); update r1, save all param regs (r1+112);
|
||||
allocate and form minimal wrapper stack frame (param area = 8*8);
|
||||
r3 = call hook_address (ctx, called_func);
|
||||
restore params regs (r1+112), r1, lr (r1+16); ctr=r11; b *ctr */
|
||||
void *_MIR_get_wrapper (MIR_context_t ctx, MIR_item_t called_func, void *hook_address) {
|
||||
static uint32_t prologue[] = {
|
||||
0x7c0802a6, /* mflr r0 */
|
||||
0xf8010010, /* std r0,16(r1) */
|
||||
0xf821fee9, /* stdu r1,-280(r1): 6(frame start) + 8(gp args) + 13(fp args) + 8(param area) */
|
||||
0xf8610070, /* std r3,112(r1) */
|
||||
0xf8810078, /* std r4,120(r1) */
|
||||
0xf8a10080, /* std r5,128(r1) */
|
||||
0xf8c10088, /* std r6,136(r1) */
|
||||
0xf8e10090, /* std r7,144(r1) */
|
||||
0xf9010098, /* std r8,152(r1) */
|
||||
0xf92100a0, /* std r9,160(r1) */
|
||||
0xf94100a8, /* std r10,168(r1) */
|
||||
0xd82100b0, /* stfd f1,176(r1) */
|
||||
0xd84100b8, /* stfd f2,184(r1) */
|
||||
0xd86100c0, /* stfd f3,192(r1) */
|
||||
0xd88100c8, /* stfd f4,200(r1) */
|
||||
0xd8a100d0, /* stfd f5,208(r1) */
|
||||
0xd8c100d8, /* stfd f6,216(r1) */
|
||||
0xd8e100e0, /* stfd f7,224(r1) */
|
||||
0xd90100e8, /* stfd f8,232(r1) */
|
||||
0xd92100f0, /* stfd f9,240(r1) */
|
||||
0xd94100f8, /* stfd f10,248(r1) */
|
||||
0xd9610100, /* stfd f11,256(r1) */
|
||||
0xd9810108, /* stfd f12,264(r1) */
|
||||
0xd9a10110, /* stfd f13,272(r1) */
|
||||
};
|
||||
static uint32_t epilogue[] = {
|
||||
0xe8610070, /* ld r3,112(r1) */
|
||||
0xe8810078, /* ld r4,120(r1) */
|
||||
0xe8a10080, /* ld r5,128(r1) */
|
||||
0xe8c10088, /* ld r6,136(r1) */
|
||||
0xe8e10090, /* ld r7,144(r1) */
|
||||
0xe9010098, /* ld r8,152(r1) */
|
||||
0xe92100a0, /* ld r9,160(r1) */
|
||||
0xe94100a8, /* ld r10,168(r1) */
|
||||
0xc82100b0, /* lfd f1,176(r1) */
|
||||
0xc84100b8, /* lfd f2,184(r1) */
|
||||
0xc86100c0, /* lfd f3,192(r1) */
|
||||
0xc88100c8, /* lfd f4,200(r1) */
|
||||
0xc8a100d0, /* lfd f5,208(r1) */
|
||||
0xc8c100d8, /* lfd f6,216(r1) */
|
||||
0xc8e100e0, /* lfd f7,224(r1) */
|
||||
0xc90100e8, /* lfd f8,232(r1) */
|
||||
0xc92100f0, /* lfd f9,240(r1) */
|
||||
0xc94100f8, /* lfd f10,248(r1) */
|
||||
0xc9610100, /* lfd f11,256(r1) */
|
||||
0xc9810108, /* lfd f12,264(r1) */
|
||||
0xc9a10110, /* lfd f13,272(r1) */
|
||||
0x38210118, /* addi r1,r1,280 */
|
||||
0xe8010010, /* ld r0,16(r1) */
|
||||
0x7c0803a6, /* mtlr r0 */
|
||||
};
|
||||
|
||||
VARR_TRUNC (uint8_t, machine_insns, 0);
|
||||
push_insns (ctx, prologue, sizeof (prologue));
|
||||
ppc64_gen_address (ctx, 3, ctx);
|
||||
ppc64_gen_address (ctx, 4, called_func);
|
||||
ppc64_gen_address (ctx, 5, hook_address);
|
||||
ppc64_gen_jump (ctx, 5, TRUE);
|
||||
ppc64_gen_mov (ctx, 11, 3);
|
||||
push_insns (ctx, epilogue, sizeof (epilogue));
|
||||
ppc64_gen_jump (ctx, 11, FALSE);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
-- Copyright vnmakarov see https://github.com/vnmakarov/mir/issues/2
|
||||
local function sieve()
|
||||
local i: integer, k: integer, prime: integer, count: integer
|
||||
local flags: integer[] = table.intarray(8190)
|
||||
|
||||
for iter=0,100000 do
|
||||
count = 0
|
||||
for i=0,8190 do
|
||||
flags[i] = 1
|
||||
end
|
||||
for i=0,8190 do
|
||||
if flags[i] == 1 then
|
||||
prime = i + i + 3;
|
||||
for k = i + prime, 8190, prime do
|
||||
flags[k] = 0
|
||||
end
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
if ravi.jit() then
|
||||
print('JIT ON')
|
||||
ravi.optlevel(2)
|
||||
ravi.compile(sieve, {omitArrayGetRangeCheck=1})
|
||||
end
|
||||
-- ravi.dumplua(sieve)
|
||||
-- ravi.dumpir(sieve)
|
||||
|
||||
local t1 = os.clock()
|
||||
local count = sieve()
|
||||
local t2 = os.clock()
|
||||
print("time taken ", t2-t1)
|
||||
print(count)
|
||||
assert(count == 1899)
|
Loading…
Reference in new issue