Gyoji Compiler
|
Operations inside basic blocks, the virtual instruction-set of the MIR. More...
#include <operations.hpp>
Public Types | |
enum | OperationType { OP_FUNCTION_CALL , OP_SYMBOL , OP_WIDEN_SIGNED , OP_WIDEN_UNSIGNED , OP_WIDEN_FLOAT , OP_ARRAY_INDEX , OP_DOT , OP_LOCAL_DECLARE , OP_LOCAL_UNDECLARE , OP_LOCAL_VARIABLE , OP_LITERAL_CHAR , OP_LITERAL_STRING , OP_LITERAL_INT , OP_LITERAL_FLOAT , OP_LITERAL_BOOL , OP_LITERAL_NULL , OP_ADDRESSOF , OP_DEREFERENCE , OP_NEGATE , OP_BITWISE_NOT , OP_LOGICAL_NOT , OP_SIZEOF_TYPE , OP_ADD , OP_SUBTRACT , OP_MULTIPLY , OP_DIVIDE , OP_MODULO , OP_LOGICAL_AND , OP_LOGICAL_OR , OP_BITWISE_AND , OP_BITWISE_OR , OP_BITWISE_XOR , OP_SHIFT_LEFT , OP_SHIFT_RIGHT , OP_COMPARE_LESS , OP_COMPARE_GREATER , OP_COMPARE_LESS_EQUAL , OP_COMPARE_GREATER_EQUAL , OP_COMPARE_NOT_EQUAL , OP_COMPARE_EQUAL , OP_ASSIGN , OP_JUMP_CONDITIONAL , OP_JUMP , OP_RETURN , OP_RETURN_VOID } |
Operations of the MIR virtual-machine. More... | |
Public Member Functions | |
Operation (OperationType _type, const Gyoji::context::SourceReference &_src_ref, size_t _result) | |
Operation (OperationType _type, const Gyoji::context::SourceReference &_src_ref, size_t _result, size_t _operand) | |
Operation (OperationType _type, const Gyoji::context::SourceReference &_src_ref, size_t _result, size_t _operand_a, size_t _operand_b) | |
Operation (OperationType _type, const Gyoji::context::SourceReference &_src_ref, size_t _result, size_t _operand_a, size_t _operand_b, size_t _operand_c) | |
virtual | ~Operation () |
Move along, nothing to see here. | |
void | dump (FILE *out) const |
OperationType | get_type () const |
Opcode of this operation. | |
const std::vector< size_t > & | get_operands () const |
Get the operands. | |
size_t | get_result () const |
Get the result of this operation. | |
bool | is_terminating () const |
Returns true if this is a terminating operation for a block. | |
const Gyoji::context::SourceReference & | get_source_ref () const |
Get the reference to the source which originated this operation. | |
virtual std::string | get_description () const |
Produce a description of the operation. | |
Protected Member Functions | |
void | add_operand (size_t operand) |
Add an operand. | |
Operations inside basic blocks, the virtual instruction-set of the MIR.
This represents a variation on the "Three Address Code" form. These operations make up the virtual instruction-set of the MIR virtual machine.
Each operation such as 'add' c = a + b is represented as
where '_c' is the returned value and '_a' and '_b' are the operands.
There are some operations (like function calls) that take more than three addresses, but for most, the operation performs an operation on one or two operands and returns a result.
Some operations carry additional information such as branch locations (basic-block IDs) or string literals (in the case of the literal-string opcode).
Each operation has a "Type" field representing the concrete operation being performed. The operands and result are passed in and manipulated.
The operands are identified in terms of the ID of the temporary value they operate on. Type information is carried there, not inside the operation although operations do have strict conditions on the types of the operands and the types of the returned values. Those conditions are enforced outside of this structure.
Operations of the MIR virtual-machine.
This type represents the various operations or instructions that the virtual machine of the MIR can perform. Each operation performs some basic computation on the values of the 'registers' of the machine which are given by operands. The operations also produce a result called the 'return-value' of the operation. Most operations take three operands (i.e. three-address-code) however, some operations take fewer and a few operations take more. Generally speaking, however, these operations are as primitive as it is possible to make them and most of the time, they will result in a single instruction on the target platform.
Enumerator | |
---|---|
OP_FUNCTION_CALL | Function call. Directs the MIR to call a function. The first operand is the function to call and the remaining operands are the arguments to the function. The opcode will return the same type that the function returns and the operand types will match the arguments of the function. Examples: Symbols:
main : u8(*)(u32, u8**)
Operations:
_0: (u8)(*)(u32, u8**) = symbol("main")
_1: u32 = literal-int(0)
_2: u8** = nullptr
_3: u8 = function-call (_0, _1, _2)
|
OP_SYMBOL | Load a symbol from the symbol-table. This opcode directs the MIR to load a symbol from the symbol table. This will be either a function pointer (to be used by the OP_FUNCTION_CALL opcode) or by another opcode in evaluating an expression like a global variable. This opcode will return the type of the symbol found. In the case of a function, this is the function-pointer type signature and in the case of a global variable, it will be whatever type that variable is declared as. Symbols:
main : u8(*)(u32, u8**)
Operations:
_0: (u8)(*)(u32, u8**) = symbol("main")
_1: u32 = literal-int(0)
_2: u8** = nullptr
_3: u8 = function-call (_0, _1, _2)
|
OP_WIDEN_SIGNED | Widen a signed integer to a larger type. This opcode widens a value from a signed integer to another signed integer type by "sign-extending" the value. For example, it can convert a signed i8 to a signed i32. This operation takes a single operand, the variable to be extended, and a type to which it should be extended. The type should also be a signed integer. The result will be the value of the integer, sign-extended to the type given. It is an error to pass any operand which is not a signed integer or a Type which is not a signed integer type. Examples: _0 : i8
_1: i16 = widen-signed ( _0, i16 )
_2: i32 = widen-signed ( _1, i32 )
_3: i64 = widen-signed ( _2, i64 )
|
OP_WIDEN_UNSIGNED | Widen an unsigned integer to a larger type. This opcode widens a value from an unsigned integer to another unsigned integer type by "zero-extending" the value. For example, it can convert an unsigned u8 to a u64 by zero-extending it. The result will be the value of the unsigned integer, zero-extended to the type given. It is an error to pass any operand which is not an unsigned integer or a Type which is not an unsigned integer type. Examples: _0 : u8
_1: u16 = widen-unsigned ( _0, u16 )
_2: u32 = widen-unsigned ( _1, u32 )
_3: u64 = widen-unsigned ( _2, u64 )
|
OP_WIDEN_FLOAT | Widen a floating-point integer to wider type. This opcode widens a value from a floating-point (f32) to a double-precision floating point (f64). The result will be the value of the floating-point, converted and widened to larger type (f64). It is an error to pass any operand which is not a floating-point number of a Type which is not a floating-point number. Examples: _0 : f32
_1: f64 = widen-float ( _0, f64 )
|
OP_ARRAY_INDEX | Access a value inside an array. This opcode allows access to values stored inside an array. It takes two operands, the first must be an array and the second is an index into the array. The index into the array must be an unsigned integer type. The return-value of the opcode is of the same type as the elements of the array. The returned value is an lvalue so it can be accessed or assigned. Examples: _0 : local-declare ( u8[] )
_1 : u32 = literal-int( 2 )
_3 : u8 = array-index( _0 _1 )
|
OP_DOT | Access a value inside a class. This opcode is used to access values contained in a class structure. The access may be for values (member variables) or functions (methods). For member variables, returned type is the same as the same as the type of the member variable and the value is an lvalue so it can be the target of an assignment. For methods, the returned type is a function pointer whose signature is the same as the method and the value is not an lvalue meaning that it can be read, but not written to. Examples: _0 : local-declare ( class { u8 : c } )
_1 : u8 = member-access ( _0 "c" )
|
OP_LOCAL_DECLARE | Declare local variable. This declares a local variable on the stack of a given type. This takes no opcodes and returns no value. Instead, it operates on the virtual machine by bringing a variable into scope so that it can be loaded and stored by other opcodes. This opcode has the name of the variable to declare and the type of variable it is so that it can be appropriately allocated. For example, this declaration: {
...
u8 foo;
...
}
will result in this opcode: local-declare (foo)
|
OP_LOCAL_UNDECLARE | Undeclare local variable. This de-allocates a local variable on the stack of a given type. This takes no opcodes and returns no value. Instead, it operates on the virtual machine by removing that variable from scope. Access to this variable after it has gone out of scope is an error. This opcode carries the name of the variable to de-allocate. {
...
u8 foo;
...
}
will result in this opcode when
foo goes out of scope.
@code{.unparsed}
local-undeclare (foo)
|
OP_LOCAL_VARIABLE | Load value from variable. This loads the value of a variable from its storage and places the result in the return-value. For example, if the variable 'foo' is declared as a u8, the following will result when the value of 'foo' is declared. _0: u8 = load (foo)
This opcode results in an "lvalue", that is, the variable 'foo' can also be the target of an assignment. This opcode takes no operands. |
OP_LITERAL_CHAR | Loads a literal character constant. This loads a literal character constant into the return-value of the operation. _0: u8 = literal-char( 'x' )
|
OP_LITERAL_STRING | Loads a string constant. This loads a pointer to a u8 that points to a string literal defined in the static constant data area. The data carried here is the exact binary data to be placed into the static section. Any C-style escape sequences should already have been interpreted before this is used. No assumptions about the data are made except that it is a null-terminated sequence of bytes. _0: u8* = literal-string( "Hello World" )
|
OP_LITERAL_INT | Loads an integer constant. This loads an integer constant into the return-value of the operand. _0: u32 = literal-float( 17 u32 )
|
OP_LITERAL_FLOAT | Loads an integer constant. This loads a f32 or f64 floating-point constant into the return-value of the operand. _0: f32 = literal-float( 17.8 f32 )
|
OP_LITERAL_BOOL | Loads a boolean literal value. This loads 'true' or 'false' into a literal constant. _0: bool = literal_bool( true )
|
OP_LITERAL_NULL | Loads a null pointer literal constant. This loads a literal 'null' pointer value for use in pointer comparison and assingments. _0: bool = literal_null( )
|
OP_ADDRESSOF | Returns the address of the given variable. This opcode is the equivalent of the C++ '&' operation. It returns the address of the value stored at the location of the operand. The operand must be an lvalue (i.e. it must be assignable). This is not a valid operation to perform on intermediate values, but only on variables. _0: u32 = local-load( foo )
_1: u32* = addressof( _0 )
The operand must be an lvalue (i.e. it must be assignable). |
OP_DEREFERENCE | De-references the given pointer. This opcode is the equivalent of the C++ '*' operation. It returns an lvalue for the value stored at the location referenced by the given pointer. _0: u32* = local-load( foo )
_1: u32 = dereference( _0 )
The resulting dereferenced value will be an lvalue (i.e. it can be used in an assignment operation) |
OP_NEGATE | Negates a signed integer. This opcode negates a signed or floating-point integer. |
OP_BITWISE_NOT | Performs a bitwise not on an unsigned integer. This opcode performs a bitwise not of the unsigned integer operand. It always returns a value the same type as the operand. |
OP_LOGICAL_NOT | This opcode performs a boolean NOT of the given operand. This opcode performs a logical (boolean) NOT of the given operand. It always takes a single boolean operand. |
OP_SIZEOF_TYPE | Returns the size of a specific type. This opcode returns a u64 representing the size of the operand of the given type. |
OP_ADD | Arithmetic add. This opcode adds two nubmers together and returns the sum modulo 2^n where n is the number of bits in the operands. Note that this opcode requires that both operands are of the same type: that they are either both signed, or both float and that the size of the values is the same. If widening or casting is needed, that must be performed first using one of the WIDEN or CAST instructions. |
OP_SUBTRACT | Arithmetic subtract. This opcode subtracts two nubmers together and returns the sum modulo 2^n where n is the number of bits in the operands. Note that this opcode requires that both operands are of the same type: that they are either both signed, both unsigned, or both float and that the size of the values is the same. If widening or casting is needed, that must be performed first using one of the WIDEN or CAST instructions. |
OP_MULTIPLY | Arithmetic multiply. This opcode multiplies by nubmers together and returns the product modulo 2^n where n is the number of bits in the operands. Note that this opcode requires that both operands are of the same type: that they are either both signed, both unsigned, or both float and that the size of the values is the same. If widening or casting is needed, that must be performed first using one of the WIDEN or CAST instructions. |
OP_DIVIDE | Arithmetic multiply. This opcode divides two nubmers and returns the quotient modulo 2^n where n is the number of bits in the operands. Note that this opcode requires that both operands are of the same type: that they are either both signed, both unsigned, or both float and that the size of the values is the same. If widening or casting is needed, that must be performed first using one of the WIDEN or CAST instructions. Note that divide-by-zero is not inherently protected at the MIR level. |
OP_MODULO | Modulo. This opcode calculates the remainder when dividing two numbers modulo 2^n where n is the number of bits in the operands. Note that this opcode requires that both operands are of the same type: that they are either both signed, both unsigned. Floating-point operands are not supported. If widening or casting is needed, that must be performed first using one of the WIDEN or CAST instructions. Note that divide-by-zero is not inherently protected at the MIR level. |
OP_LOGICAL_AND | Logical AND. This opcode performs a boolean AND on the two opcodes and returns a boolean value as the result. Both operands must be boolean values. |
OP_LOGICAL_OR | Logical OR. This opcode performs a boolean OR on the two opcodes and returns a boolean value as the result. Both operands must be boolean values. |
OP_BITWISE_AND | Bitwise AND. This opcode performs a bitwise AND on the two opcodes. Both opcodes must be unsigned integer types and each bit in the representation of the integers is ORed together producing an unsigned integer result of the same size as the operands. Both integer operands must be of the same size. |
OP_BITWISE_OR | Bitwise OR. This opcode performs a bitwise OR on the two opcodes. Both opcodes must be unsigned integer types and each bit in the representation of the integers is ORed together producing an unsigned integer result of the same size as the operands. Both integer operands must be of the same size. |
OP_BITWISE_XOR | Bitwise XOR. This opcode performs a bitwise XOR on the two opcodes. Both opcodes must be unsigned integer types and each bit in the representation of the integers is ORed together producing an unsigned integer result of the same size as the operands. Both integer operands must be of the same size. |
OP_SHIFT_LEFT | Bitwise Shift left. This opcode performs a bitwise shift left of the first operand by the number of bits in the second operand (modulo the number of bits in the first operand). Both operands must be unsigned integers. For example, if you shift left on a u8 by 9 bits, the result is the same as shifting left by 1 bit because 9 == 1 (modulo 8) and shifting a u8 by 9 bits would not be meaningful. |
OP_SHIFT_RIGHT | Bitwise Shift Right. This opcode performs a bitwise shift right of the first operand by the number of bits in the second operand (modulo the number of bits in the first operand). Both operands must be unsigned integers. For example, if you shift left on a u16 by 17 bits, the result is the same as shifting right by 1 bit because 17 == 1 (modulo 16) and shifting a u8 by 17 bits would not be meaningful. |
OP_COMPARE_LESS | Compare numbers for less-than. This opcode performs a comparison of the two operands and returns a boolean true if the first operand is numerically less than the second. Both operands must be numeric types of the same and must be both signed, unsigned, or floating-point numbers. |
OP_COMPARE_GREATER | Compare numbers for greater-than. This opcode performs a comparison of the two operands and returns a boolean true if the first operand is numerically greater than the second. Both operands must be numeric types of the same and must be both signed, unsigned, or floating-point numbers. |
OP_COMPARE_LESS_EQUAL | Compare numbers for less-than or equal to. This opcode performs a comparison of the two operands and returns a boolean true if the first operand is numerically less than or equal to the second. Both operands must be numeric types of the same and must be both signed, unsigned, or floating-point numbers. |
OP_COMPARE_GREATER_EQUAL | Compare numbers for greater-than or equal to. This opcode performs a comparison of the two operands and returns a boolean true if the first operand is numerically greater than or equal to the second. Both operands must be numeric types of the same and must be both signed, unsigned, or floating-point numbers. |
OP_COMPARE_NOT_EQUAL | Compare numbers for not equal-to. This opcode performs a comparison of the two operands and returns a boolean true if the first operand is numerically not equal to the second. Both operands must be numeric types of the same and must be both signed, unsigned, or floating-point numbers. |
OP_COMPARE_EQUAL | Compare numbers for equality. This opcode performs a comparison of the two operands and returns a boolean true if the first operand is numerically equal to the second. Both operands must be numeric types of the same and must be both signed, unsigned, or floating-point numbers. |
OP_ASSIGN | Assign value. This opcode assigns the value of the second operand to the lvalue represented by the first operand. The value being assigned must be an lvalue which means that it must be a variable or an expression that can be assigned to such as a pointer-indirection to a storage location. |
OP_JUMP_CONDITIONAL | Conditional Jump. This opcode performs a jump to one of two BasicBlocks based on the condition found in the first operand. The first operand must be a boolean value indicating which jump path to take. The second operand is the location to jump to if the condition is true and the third operand is the location to jump to if the condition if false. |
OP_JUMP | Jump (Unconditional) This opcode performs a jump to another BasicBlock unconditionally. The first (and only) operand is the ID of the basic block to jump to. |
OP_RETURN | Return from function. This opcode returns from the current function, returning the stack and control to the calling function. A return-value is also provided in the first (and only) opcode indicating the value to return to the caller. Note that this opcode does not provide a value because no further control-flow is possible in this basic-block or in this function at all, so providing a return-value from this opcode would be meaningless. |
OP_RETURN_VOID | Return from function without supplying a value. This opcode returns from the current function, returning the stack and control to the calling function. A return-value is not supplied in this case (or returning the 'void' value). |
Operation::Operation | ( | OperationType | _type, |
const Gyoji::context::SourceReference & | _src_ref, | ||
size_t | _result | ||
) |
Constructs an opcode of the given type, producing the given result. No operands are given.
Operation::Operation | ( | OperationType | _type, |
const Gyoji::context::SourceReference & | _src_ref, | ||
size_t | _result, | ||
size_t | _operand | ||
) |
Constructs an opcode of the given type, producing the given result and taking a single operand. This is useful for constructing unary operations.
Operation::Operation | ( | OperationType | _type, |
const Gyoji::context::SourceReference & | _src_ref, | ||
size_t | _result, | ||
size_t | _operand_a, | ||
size_t | _operand_b | ||
) |
Constructs an opcode of the given type, producing the given result and taking two operands. This is useful for constructing binary operations.
Operation::Operation | ( | OperationType | _type, |
const Gyoji::context::SourceReference & | _src_ref, | ||
size_t | _result, | ||
size_t | _operand_a, | ||
size_t | _operand_b, | ||
size_t | _operand_c | ||
) |
Constructs an opcode of the given type, producing the given result and taking three operands. This is useful for some of the exceptional opcodes like conditional jump instructions.
|
virtual |
Move along, nothing to see here.
Move along, nothing to see here.
|
protected |
Add an operand.
This method adds an operand to the operation. It is used exclusively internally when constructing operations.
void Operation::dump | ( | FILE * | out | ) | const |
This method prints debuginng information about the opcode to the given file handle.
|
virtual |
Produce a description of the operation.
This method returns a full description of the operation including the return-value, operands, and any other ancilary information about the operation. This is a string that is constructed dynamically when needed and is used almost exclusively by the "dump" method to provide debuginng information.
Reimplemented in Gyoji::mir::OperationCast, Gyoji::mir::OperationSymbol, Gyoji::mir::OperationDot, Gyoji::mir::OperationLocalVariable, Gyoji::mir::OperationLiteralChar, Gyoji::mir::OperationLiteralString, Gyoji::mir::OperationLiteralInt, Gyoji::mir::OperationLiteralFloat, Gyoji::mir::OperationLiteralBool, Gyoji::mir::OperationLiteralNull, Gyoji::mir::OperationJumpConditional, Gyoji::mir::OperationJump, Gyoji::mir::OperationReturn, Gyoji::mir::OperationReturnVoid, Gyoji::mir::OperationLocalDeclare, and Gyoji::mir::OperationLocalUndeclare.
const std::vector< size_t > & Operation::get_operands | ( | ) | const |
Get the operands.
This method returns the operands provided when the operation was constructed.
size_t Operation::get_result | ( | ) | const |
Get the result of this operation.
This returns the temporary variable ID for where the result of this operation should be placed.
const Gyoji::context::SourceReference & Operation::get_source_ref | ( | ) | const |
Get the reference to the source which originated this operation.
This returns a reference to the source translation unit that originated this operation. In most cases, this is the location of the operator found in the source-file such as a '*' or a '+' symbol.
Operation::OperationType Operation::get_type | ( | ) | const |
Opcode of this operation.
This method returns the opcode of the this operation.
bool Operation::is_terminating | ( | ) | const |
Returns true if this is a terminating operation for a block.
This function returns true if the operation would terminate a basic block. This happens if the operation is a jump, conditional branch, or return statement.