[Next] [Previous] [Top]

RTL Grammar

4 Statements


RTL statements are the following:

rtl_stmts	::=	{ rtl_stmt ";" }
rtl_stmt	::=	alu
	|	vector_op
	|	field_op
	|	ptr_op
	|	allocation
	|	exception_scope
	|	label
	|	goto
	|	fn_call
	|	return
	|	fatal
	|	static_info
Many 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" representation
Local 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 representation
Arguments 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 | string
Vector 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 | integer
Field (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_name
Simple 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_name
High-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_name
The 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_name
Various 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_literal
The 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_name
Return 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_info
Currently, 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 ")"

RTL Grammar - 25 MARCH 1997
[Next] [Previous] [Top]

Generated with Harlequin WebMaker