RTL Grammar
rtl_stmts ::= { rtl_stmt ";" } rtl_stmt ::= alu | vector_op | field_op | ptr_op | allocation | exception_scope | label | goto | fn_call | return | fatal | static_infoMany RTL statements may be prefaced with a declaration. These declarations are used to pass along type information that will be needed to declare variables in the C++ code we generate as output. A given variable should be declared at the first textual place it is assigned or read, but the scope of the declaration is the entire method. References
decl ::= "decl" representationLocal variable declarations, assignments, unary and binary operations, and conversions (which specify the target representation to convert to).
alu ::= local_decl | assign | unop | binop | conversion local_decl ::= [decl] rtl_name assign ::= [decl] rtl_name ":=" value unop ::= [decl] rtl_name ":=" unop_op value binop ::= [decl] rtl_name ":=" value binop_op value conversion ::= [decl] rtl_name ":=" "convert" value representationArguments to RTL operators can name RTL variables, can name undeclared variables (in which case they are assumed to name global variables of type
void*
that are defined by the run-time system and the header files included with the generated code), can mention integer, float, double, char, or string literals, and can mention language-specific void, true, false, and null-pointer values.
value ::= rtl_name | literal | "void" | "true" | "false" | "null" literal ::= integer | float | double | character | stringVector operations. Stores or fetches from a vector, array, or string object. The representation of the element is specified as part of the operation. It is possible to mark a vector location as immutable, meaning that the location won't be modified in the future (except possibly for explicit stores later in this procedure).
vector_op ::= vector_load | vector_store vector_load ::= [decl] rtl_name ":=" vector_location vector_store ::= [decl] vector_location ":=" value vector_location ::= rtl_name "[" index "]" representation ["is_immutable"] index ::= rtl_name | integerField (instance variable) operations. Stores or fetches from a declared field of an object. Field contents are considered immutable when the corresponding field declaration specified an immutable field.
field_op ::= field_load | field_store field_load ::= [decl] rtl_name ":=" field_location field_store ::= [decl] field_location ":=" value field_location ::= rtl_name "." rtl_name "@" rtl_nameSimple pointer operations. Used to access language-level arrays, structs, and so on, if they've been translated down into this low level. The representation specifies the representation of the pointer target value being loaded or stored, and the index is a byte offset from the base pointer. Pointer target locations can be marked immutable, as with vector locations.
ptr_op ::= ptr_load | ptr_store | addr_op ptr_load ::= [decl] rtl_name ":=" ptr_location ptr_store ::= ptr_location ":=" value ptr_location ::= "*" "(" rtl_name ["+" index] ")" representation ["is_immutable"]Pointers can be created by taking the address of a variable or of a procedure.
addr_op ::= [decl] rtl_name ":=" "&" rtl_name | [decl] rtl_name ":=" "&_proc" rtl_nameHigh-level allocation of new data structures. Object allocation can be used for classes with associated field declarations specifying the class's structure and
front_end_does_field_layout
turned off; various class-testing operations and field access operations can be applied to these objects. The vector allocator operations are Cecil-specific. The array allocator is used to allocate array-like objects, given the name of the array class, the representation of the elements, and the length of the array. The array can be defined as mutable or immutable. Vector and array objects are indexed using the vector load and store operations above, and the num_elems unary operator can be applied to vectors and arrays.
allocation ::= [decl] rtl_name ":=" obj_allocator | [decl] rtl_name ":=" vec_allocator | [decl] rtl_name ":=" array_allocator obj_allocator ::= "new" rtl_name vec_allocator ::= vec_alloc_op value vec_alloc_op ::= "new_m_vector" | "new_i_vector" | "new_m_string" | "new_i_string" | "new_m_float_vector" | "new_i_float_vector" | "new_m_word_vector" | "new_i_word_vector" array_allocator ::= array_alloc_op rtl_name representation value array_alloc_op ::= "new_m_array" | "new_i_array"Exceptions can be raised by calls, sends, and explicit raises. The scope of a handler for some exceptions is bracketed by
enter_
and exit_exn_scope
RTL statements. These statements name the label where the innermost-enclosing exception handler for this scope begins. For languages where exceptions_through_longjmp
is set, the runtime system is assumed to select the right handler if an exception is raised; the enter_exn_scope provides enough information to the runtime system to make this possible. Otherwise, exceptions propagate outwards through exception scopes incrementally, and each exception handling scope is responsible for forwarding control to the enclosing exception handling scope if it is not the right handler.
exception_scope ::= enter_exn_scope | exit_exn_scope enter_exn_scope ::= "enter_exn_scope" label_name rtl_name ["(" rtl_name ")"] ["[" value "," value "," value "]"] exit_exn_scope ::= "exit_exn_scope" label_nameThe
enter_exn_scope
operator implicitly declares the variable to which the "name" of the exception being raised will be bound, and optionally declares the name which will be bound to the argument of the raised exception; these names are expected to be used in the exception handling code. For Modula-3, three additional parameters are needed, giving the address of the exception record, the address of the module's exception handler list, and the class of the exception. (Other languages will presumably need extensions to support arguments appropriate for their runtime system implementation of exceptions.)Labels that start exception handlers must be flagged as such.
label ::= ("label" | "exn_handler_label") label_name label_name ::= rtl_nameVarious kinds of unconditional and conditional branches.
is_class
is true iff the value is an instance of the named class; inherits_from
extends this test to include subclasses as well. inherits_from
can take a variable name (a computed CecilMap*
) as an argument instead of a class name constant.
goto ::= cond_goto | uncond_goto | case_goto | raise cond_goto ::= "if" test "goto" label_name test ::= unop_op value | value binop_op value | value "inherits_from" rtl_name | value "is_class" rtl_name uncond_goto ::= "goto" label_name
case_goto
implements an N-way branch. The argument value should be an integer in the range [0..N-1], and one of the N target labels is jumped to based on this value.
case_goto ::= "case" value "goto" "[" label_names "]" label_names ::= label_name {"," label_name}Exceptions are raised using the
raise
command. The first operand evaluates to the exception "name" (or exception object, for e.g. C++ and Java), and the second optional value is the argument to the exception. Control transfers unconditionally to the appropriate enclosing exception handler when the raise is executed.
raise ::= "raise_exn" value ["(" value ")"]Various kinds of procedure calls. The result of a call can be omitted (e.g. if the callee returns no value). All kinds of calls allow a marker blocking inlining of the call as well as an indication of whether the call can raise exceptions (a.k.a. produce a non-local return). (If
sends_can_raise
is set, then sends and table sends default to raising exceptions, otherwise calls default to not raising exceptions.)
fn_call ::= [[decl] rtl_name ":="] (call | send | table_send) ["no_inline"] ["can_raise_exns" | "no_exns"]Calls are regular direct calls. The callee is either a regular named procedure (potentially one defined in RTL with a method decl, in which case it can be inlined;
call_external
blocks this check), or a value that evaluates to the address of the procedure to call.
call ::= call_desc "(" [values] ")" call_desc ::= "call" rtl_name | "call_external" rtl_name | "call_indirect" value values ::= value {"," value}Sends are messages, and search (using Cecil's inheritance rules) the named method set for the most specific applicable method, based on the run-time classes of the send arguments. Send RTLs can only be used if
uses_PIC_based_runtime
is set.
send ::= "send" rtl_name "(" [values] ")"Table sends are similar to sends, except that they get translated into the appropriate table lookup sequence (precomputed by the language front-ends) if they cannot be statically bound.
table_send ::= "table_send" rtl_name "(" [values] ")" "[" table_index "]" [ "id_offset" "(" int_literal ")" ] [static_type_info] table_index ::= int_literalThe address of a lookup table is fetched from the object specified as the first argument of the send, at the byte offset specified by the
id_offset
clause. If no id_offset
is specified, the table address is assumed to be in the first word of the object. The table_index
specifies the index into the table, giving a table entry to use for dispatching this send. For example:
t1 := table_send my_message(t2, t3) [2] id_offset(4)This means: fetch the address of the table from
table_base := *(t2+4)
, then use the table entry at table_base+(2*sizeof(table_entry))
to dispatch this send. The precise way in which the table is used to dispatch the send is language dependent. Currently, Vortex supports two kinds of tables: a simple table with address-sized entries that contain the address of a routine to call, and a more complicated, C++-style table format that contains both the address of a routine and an adjustment value that should be added to the first argument before entering the callee.The static type of the receiver argument of a table_send can be specified by naming the class that the receiver is known to inherit from.
static_type_info ::= "static_type" "(" static_type ")" static_type ::= rtl_nameReturn statements.
return ::= "return" [value]Fatal errors: abort execution with a string message.
fatal ::= "fatal" "(" string ")"Assertions about the static properties of a value.
static_info ::= "assert_type" rtl_name static_type_infoCurrently, static information specifies either an exact class for the variable, or specifies a cone of possible classes (and the value could be an instance of the class or any subclass of the class), or gives another variable whose type to copy. The optimizer uses this information to reason about the possible values that could be stored in the given variable.
static_type_info ::= "map" "(" rtl_name ")" | "cone" "(" rtl_name ")" | "same_as" "(" rtl_name ")"
Generated with Harlequin WebMaker