parrotcode: Parrot Intermediate Representation | |
Contents | Documentation |
docs/pdds/pdd19_pir.pod - Parrot Intermediate Representation
$Revision$
This document outlines the architecture and core syntax of Parrot Intermediate Representation (PIR).
PIR is a stable, middle-level language intended both as a target for the generated output from high-level language compilers, and for human use developing core features and extensions for Parrot.
A valid PIR program consists of a sequence of statements, directives, comments and empty lines.
A statement starts with an optional label, contains an instruction, and is terminated by a newline (<NL>). Each statement must be on its own line.
[label:] [instruction] <NL>
An instruction may be either a low-level opcode or a higher-level PIR operation, such as a subroutine call, a method call, a directive, or PIR syntactic sugar.
A directive provides information for the PIR compiler that is outside the normal flow of executable statements. Directives are all prefixed with a ".", as in .local
or .sub
.
Comments start with #
and last until the following newline. PIR also allows comments in Pod format. Comments, Pod content, and empty lines are ignored.
Identifiers start with a letter or underscore, then may contain additionally letters, digits, and underscores. Identifiers don't have any limit on length at the moment, but some sane-but-generous length limit may be imposed in the future (256 chars, 1024 chars?). The following examples are all valid identifiers.
a
_a
A42
Opcode names are not reserved words in PIR, and may be used as variable names. For example, you can define a local variable named print
. [See RT #24251]
{{ NOTE: The use of ::
in identifiers is deprecated. [See RT #48735] }}
A label declaration consists of a label name followed by a colon. A label name conforms to the standard requirements for identifiers. A label declaration may occur at the start of a statement, or stand alone on a line, but always within a subroutine.
A reference to a label consists of only the label name, and is generally used as an argument to an instruction or directive.
A PIR label is accessible only in the subroutine where it's defined. A label name must be unique within a subroutine, but it can be reused in other subroutines.
goto label1
...
label1:
There are two ways of referencing Parrot's registers. The first is through named local variables declared with .local
.
.local pmc foo
The type of a named variable can be int
, num
, string
or pmc
, corresponding to the types of registers. No other types are used. [See RT#42769]
The second way of referencing a register is through a register variable $In
, $Sn
, $Nn
, or $Pn
. The capital letter indicates the type of the register (integer, string, number, or PMC). n consists of digit(s) only. There is no limit on the size of n. There is no direct correspondence between the value of n and the position of the register in the register set, $P42
may be stored in the zeroth PMC register, if it is the only register in the subroutine.
{{DEPRECATION NOTE: PIR will no longer support the old PASM-style syntax for registers without dollar signs: In
, Sn
, Nn
, Pn
. RT#57638}}
Constants may be used in place of registers or variables. A constant is not allowed on the left side of an assignment, or in any other context where the variable would be modified.
'
). They are taken to be ASCII encoded. No escape sequences are processed."
). A "
inside a string must be escaped by \
. The default encoding for a double-quoted string constant is 7-bit ASCII, other character sets and encodings must be marked explicitly using a charset or encoding flag. $S0 = <<"EOS"
...
EOS
function(<<"END_OF_HERE", arg)
...
END_OF_HERE
.return(<<'EOS')
...
EOS
.yield(<<'EOS')
...
EOS
function(<<'INPUT', <<'OUTPUT', 'some test')
...
INPUT
...
OUTPUT
ascii
(the default), binary
, unicode
(with UTF-8 as the default encoding), and iso-8859-1
.Inside double-quoted strings the following escape sequences are processed.
\xhh 1..2 hex digits
\ooo 1..3 oct digits
\cX control char X
\x{h..h} 1..8 hex digits
\uhhhh 4 hex digits
\Uhhhhhhhh 8 hex digits
\a, \b, \t, \n, \v, \f, \r, \e, \\, \"
set S0, utf8:unicode:"«"
42
) and numbers (3.14159
) may appear as constants. 0x
and 0b
denote hex and binary constants respectively. .local int i, j
:unique_reg
modifier will force the register allocator to associate the identifier with a unique register for the duration of the subroutine. .lex '$a', $P0
$P1 = new 'Integer'
These two opcodes have an identical effect:
$P0 = $P1
store_lex '$a', $P1
And these two opcodes also have an identical effect:
$P1 = $P0
$P1 = find_lex '$a'
.const
above, but the defined constant is globally accessible. .sub <identifier> [:<flag> ...]
.sub <quoted string> [:<flag> ...]
.end
..sub
. .namespace [ <key>? ]
key: <identifier> [';' <identifier>]*
.namespace [ ]
loadlib
opcode, which does the same at run time.:load
, so there is no need to call loadlib
at runtime..loadlib
instead. .loadlib 'dynlexpad'
.HLL 'Foo'
.HLL_map 'LexPad' = 'DynLexPad'
.sub main :main
...
<#line <integer
<string>>>. See [RT#45857], [RT#43269], and [RT#47141]. }}.namespace
and .endnamespace
are deprecated. They were a hackish attempt at implementing scopes in Parrot, but didn't actually turn out to be useful.}} local x = 1;
print(x); # prints 1
do # open a new namespace/scope block
local x = 2; # this x hides the previous x
print(x); # prints 2
end # close the current namespace
print(x); # prints 1 again
abs
, not
, bnot
, bnots
, and neg
are also changed to use a n_
prefix. .pragma n_operators 1
.sub foo
...
$P0 = $P1 + $P2 # n_add $P0, $P1, $P2
$P2 = abs $P0 # n_abs $P2, $P0
BEGIN
in Perl 5.examples/shootout/revcomp.pir
contains the following (slightly abbreviated) definition: .sub tr_00_init :immediate
.local pmc tr_array
tr_array = new 'FixedIntegerArray'
tr_array = 256
## [code to initialize tr_array omitted.]
.return (tr_array)
.end
tr_array
is stored in the bytecode file in place of the sub. Other subs may then do: .const .Sub tr_00 = 'tr_00_init'
load_bytecode
instruction from another file).main.pir
contains: .sub main
load_bytecode 'foo.pir'
.end
foo.pir
contains: .sub foo :immediate
print '42'
.end
.sub bar :postcomp
print '43'
.end
foo.pir
will run both foo
and bar
. On the other hand, executing main.pir
will run only foo
. If foo.pir
is compiled to bytecode, only foo
will be run, and loading foo.pbc
will not run either foo
or bar
. .sub bar :method
.sub bar :method('foo')
.sub
is a method, added as a method in the class that corresponds to the current namespace, and not stored in the namespace. In the method body, the object PMC can be referred to with self
.:method
the method is stored with that name instead of the .sub
name. .sub bar :vtable
.sub bar :vtable('foo')
.sub
overrides a vtable function, and is not stored in the namespace. By default, it overrides a vtable function with the same name as the .sub
name. To override a different vtable function, use :vtable('...')
. For example, to have a .sub
named ToString also be the vtable function get_string
), use :vtable('get_string')
.self
, as with the :method flag..sub
is lexically nested within the sub known by subname.:outer
, even though several subroutines in the file may have the same name (because they are multi, or in different namespaces)..meth_call
..invocant
directive..begin_return
and .end_return
, specify one or more of the return value(s) of the current subroutine. Available flags: :flat
, :named
..begin_call
and .call
, specify an argument to be passed. Available flags: :flat
, :named
..call
and .end_call
, specify where one or more return value(s) should be stored. Available flags: :slurpy
, :named
, :optional
, and :opt_flag
..local
, into which parameter(s) of the current subroutine should be stored. Available flags: :slurpy
, :named
, :optional
, :opt_flag
and :unique_reg
.See PDD03 for a description of the meaning of the flag bits SLURPY
, OPTIONAL
, OPT_FLAG
, and FLAT
, which correspond to the calling convention flags :slurpy
, :optional
, :opt_flag
, and :flat
.
Using the push_eh
op you can install an exception handler. If an exception is thrown, Parrot will execute the installed exception handler. In order to retrieve the thrown exception, use the .get_results
directive. This directive always takes 2 arguments: an exception object and a message string.
{{ NOTE: Wouldn't it be more useful to make this flexible, or at least only the exception object? The message can be retrieved from the exception object. See RT #57436 }}
push_eh handler
...
handler:
.local pmc exception
.local string message
.get_results (exception, message)
...
This is syntactic sugar for the get_results
op, but any flags set on the targets will be handled automatically by the PIR compiler. The .get_results
directive must be the first instruction of the exception handler; only declarations (.lex, .local) may come first.
Any PASM opcode is a valid PIR instruction. In addition, PIR defines some syntactic shortcuts. These are provided for ease of use by humans producing and maintaining PIR code.
branch
to identifier (label or subroutine name). goto END
<, <=, ==, != >= >
. which translate to the PASM opcodes lt
, le
, eq
, ne
, ge
or gt
. If var1 relop var2 evaluates as true, jump to the named identifier.<, <=, ==, != >= >
. Unless var1 relop var2 evaluates as true, jump to the named identifier.!
(NOT), -
(negation) and ~
(bitwise NOT).+
(addition), -
(subtraction), *
(multiplication), /
(division), %
(modulus) and **
(exponent). Binary .
is concatenation and only valid for string arguments.<<
and >>
are arithmetic shifts left and right. >>>
is the logical shift right.&&
(AND), ||
(OR) and ~~
(XOR).&
(bitwise AND), |
(bitwise OR) and ~
(bitwise XOR).<, <=, ==, != >= >
.<var1> = <var1> <op> <var2>
. Where op is called an assignment operator and can be any of the following binary operators described earlier: +
, -
, *
, /
, %
, .
, &
, |
, ~
, <<
, >>
or >>>
.set
operation for PMCs or a substring operation for string arguments and an integer key...
notation in keys is deprecated [See RT #48561], so this syntactic sugar for slices is also deprecated. See the (currently experimental) slice
opcode instead. }}key
is: <var1> .. <var2>
var1
and ending at var2
. .. <var2>
var2
. <var1> ..
var1
to the end of the array.set
operation.substr
op with a length of 1. }}=
, and all remaining arguments go after the opcode name. For example: new $P0, 'Type'
$P0 = new 'Type'
OUT
parameter. [this restriction unimplemented: RT #36283] .begin_call
.arg <arg1> <flag2>
...
.call <var2>
.result <var1> <flag1>
...
.end_call
foo.bar()
where bar
is not a local variable name) are deprecated. Use a quoted string instead. See #45859. }}.return
directive that will be probably deprecated.The =
syntactic sugar in PIR, when used in the simple case of:
<var1> = <var2>
directly corresponds to the set
opcode. So, two low-level arguments (int, num, or string registers, variables, or constants) are a direct C assignment, or a C-level conversion (int cast, float cast, a string copy, or a call to one of the conversion functions like string_to_num
).
Assigning a PMC argument to a low-level argument calls the get_integer
, get_number
, or get_string
vtable function on the PMC. Assigning a low-level argument to a PMC argument calls the set_integer_native
, set_number_native
, or set_string_native
vtable function on the PMC (assign to value semantics). Two PMC arguments are a direct C assignment (assign to container semantics).
For assign to value semantics for two PMC arguments use assign
, which calls the assign_pmc
vtable function.
This section describes the macro layer of the PIR language. The macro layer of the PIR compiler handles the following directives:
.include
'<filename>'The .include
directive takes a string argument that contains the name of the PIR file that is included. The contents of the included file are inserted as if they were written at the point where the .include
directive occurs.
The include file is searched for in the current directory and in runtime/parrot/include, in that order. The first file of that name to be found is included.
{{ NOTE: the include
directive's search order is subject to change. }}
.macro
<identifier> [<parameters>]The .macro
directive starts the a macro definition named by the specified identifier. The optional parameter list is a comma-separated list of identifiers, enclosed in parentheses. See .endm
for ending the macro definition.
.endm
Closes a macro definition.
.macro_const
<identifier> (<literal>|<reg>) .macro_const PI 3.14
The .macro_const
directive is a special type of macro; it allows the user to use a symbolic name for a constant value. Like .macro
, the substitution occurs at compile time. It takes two arguments (not comma separated), the first is an identifier, the second a constant value or a register.
The macro layer is completely implemented in the lexical analysis phase. The parser does not know anything about what happens in the lexical analysis phase.
When the .include
directive is encountered, the specified file is opened and the following tokens that are requested by the parser are read from that file.
A macro expansion is a dot-prefixed identifier. For instance, if a macro was defined as shown below:
.macro foo(bar)
...
.endm
this macro can be expanded by writing .foo(42)
. The body of the macro will be inserted at the point where the macro expansion is written.
A .macro_const
expansion is more or less the same as a .macro
expansion, except that a constant expansion cannot take any arguments, and the substitution of a .macro_const
contains no newlines, so it can be used within a line of code.
The parameter list for a macro is specified in parentheses after the name of the macro. Macro parameters are not typed.
.macro foo(bar, baz, buz)
...
.endm
The number of arguments in the call to a macro must match the number of parameters in the macro's parameter list. Macros do not perform multidispatch, so you can't have two macros with the same name but different parameters. Calling a macro with the wrong number of arguments gives the user an error.
If a macro defines no parameter list, parentheses are optional on both the definition and the call. This means that a macro defined as:
.macro foo
...
.endm
can be expanded by writing either .foo
or .foo()
. And a macro definition written as:
.macro foo()
...
.endm
can also be expanded by writing either .foo
or .foo()
.
{{ NOTE: this is a change from the current implementation, which requires the definition and call of a zero-parameter macro to match in the use of parentheses. }}
Heredoc arguments are not allowed when expanding a macro. This means that the following is not allowed:
.macro foo(bar)
...
.endm
.foo(<<'EOS')
This is a heredoc
string.
EOS
{{ NOTE: This is likely because the parsing of heredocs happens later than the preprocessing of macros. Might be nice if we could parse heredocs at the macro level, but not a high priority. compilers/pirc/new can do this. }}
Using braces, { }, allows you to span multiple lines for an argument. See runtime/parrot/include/hllmacros.pir for examples and possible usage. A simple example is this:
.macro foo(a,b)
.a
.b
.endm
.sub main
.foo({ print "1"
print "2"
}, {
print "3"
print "4"
})
.end
This will expand the macro foo
, after which the input to the PIR parser is:
.sub main
print "1"
print "2"
print "3"
print "4"
.end
which will result in the output:
1234
Within the macro body, the user can declare a unique label identifier using the value of a macro parameter, like so:
.macro foo(a)
...
.label $a:
...
.endm
Within the macro body, the user can declare a local variable with a unique name.
.macro foo()
...
.macro_local int b
...
.b = 42
print .b # prints the value of the unique variable (42)
...
.endm
The .macro_local
directive declares a local variable with a unique name in the macro. When the macro .foo()
is called, the resulting code that is given to the parser will read as follows:
.sub main
.local int local__foo__b__2
...
local__foo__b__2 = 42
print local__foo__b__2
.end
The user can also declare a local variable with a unique name set to the symbolic value of one of the macro parameters.
.macro foo(b)
...
.macro_local int $b
...
.$b = 42
print .$b # prints the value of the unique variable (42)
print .b # prints the value of parameter "b", which is
# also the name of the variable.
...
.endm
So, the special $
character indicates whether the symbol is interpreted as just the value of the parameter, or that the variable by that name is meant. Obviously, the value of b
should be a string.
The automatic name munging on .macro_local
variables allows for using multiple macros, like so:
.macro foo(a)
.macro_local int $a
.endm
.macro bar(b)
.macro_local int $b
.endm
.sub main
.foo("x")
.bar("x")
.end
This will result in code for the parser as follows:
.sub main
.local int local__foo__x__2
.local int local__bar__x__4
.end
Each expansion is associated with a unique number; for labels declared with .macro_label
and locals declared with .macro_local
expansions, this means that multiple expansions of a macro will not result in conflicting label or local names.
Defining a non-unique variable can still be done, using the normal syntax:
.macro foo(b)
.local int b
.macro_local int $b
.endm
When invoking the macro foo
as follows:
.foo("x")
there will be two variables: b
and x
. When the macro is invoked twice:
.sub main
.foo("x")
.foo("y")
.end
the resulting code that is given to the parser will read as follows:
.sub main
.local int b
.local int local__foo__x
.local int b
.local int local__foo__y
.end
Obviously, this will result in an error, as the variable b
is defined twice. If you intend the macro to create unique variables names, use .macro_local
instead of .local
to take advantage of the name munging.
.sub _sub_label [<subflag>]*
.param int a
.param int b
.param int c
...
.begin_return
.return xy
.end_return
...
.end
.const .Sub $P0 = "_sub_label"
$P1 = new 'Continuation'
set_addr $P1, ret_addr
...
.local int x
.local num y
.local str z
.begin_call
.arg x
.arg y
.arg z
.call $P0, $P1 # r = _sub_label(x, y, z)
ret_addr:
.local int r # optional - new result var
.result r
.end_call
load_lib $P0, "libname"
dlfunc $P1, $P0, "funcname", "signature"
...
.begin_call
.arg x
.arg y
.arg z
.nci_call $P1 # r = funcname(x, y, z)
.local int r # optional - new result var
.result r
.end_call
... # variable decls
r = _sub_label(x, y, z)
(r1[, r2 ...]) = _sub_label(x, y, z)
_sub_label(x, y, z)
This also works for NCI calls, as the subroutine PMC will be a NCI sub, and on invocation will do the Right Thing. Instead of the label a subroutine object can be used too:
find_global $P0, "_sub_label"
$P0(args)
.namespace [ "Foo" ]
.sub _sub_label :method [,Subpragma, ...]
.param int a
.param int b
.param int c
...
self."_other_meth"()
...
.begin_return
.return xy
.end_return
...
.end
The variable "self" automatically refers to the invocating object, if the subroutine declaration contains "method".
The syntax is very similar to subroutine calls. The call is done with meth_call
which must immediately be preceded by the .invocant
:
.local pmc class
.local pmc obj
newclass class, "Foo"
new obj, class
.begin_call
.arg x
.arg y
.arg z
.invocant obj
.meth_call "_method" [, $P1 ] # r = obj."_method"(x, y, z)
.local int r # optional - new result var
.result r
.end_call
The return continuation is optional. The method can be a string constant or a string variable.
.return ( a, b ) # return the values of a and b
.return () # return no value
.return func_call() # tail call function
.return o."meth"() # tail method call
Similarly, one can yield using the .yield directive
.yield ( a, b ) # yield with the values of a and b
.yield () # yield with no value
Arguments are saved in reverse order onto the user stack:
.arg y # save args in reversed order
.arg x
call _foo #(r, s) = _foo(x,y)
.local int r
.local int s
.result r # restore results in order
.result s #
and return values are restored in argument order from there.
.sub _foo # sub foo(int a, int b)
saveall
.param int a # receive arguments from left to right
.param int b
...
.return mi # return (pl, mi), push results
.return pl # in reverse order
restoreall
ret
.end
Pushing arguments in reversed order on the user stack makes the left most argument the top of stack entry. This allows for a variable number of function arguments (and return values), where the left most argument before a variable number of following arguments is the argument count.
There are multiple implementations of PIR, each of which will meet this specification for the syntax.
N/A
N/A
See docs/imcc/macros.pod
|