IR-code

The purpose of an intermediate representation (IR) of a program is to decouple the implementation of a front-end from the implementation of a back-end. That is, front ends generate IR-code, optimizers optimize this code and lastly backends transform it into machine code or something else.

A good IR has several characteristics:

  • It should be simple enough for front-ends to generate code.
  • It should be rich enough to exploit the target instructions best.

The IR in the ppci.ir module has the following properties:

  • It is static single assignment form. Meaning a value can only be assigned once, and is then never changed. This has several advantages.
  • It contains only basic types. Structures, arrays and void types are not represented.

Top level structure

The IR-code is implemented in the ir package.

class ppci.ir.Module(name, debug_db=None)

Container unit for variables and functions.

add_external(external)

Add an externally located thing

add_function(function)

Add a function to this module

add_variable(variable)

Add a variable to this module

display()

Display this module

functions

Get all functions of this module

stats()

Returns a string with statistic information such as block count

variables

Get all variables of this module

class ppci.ir.Variable(name, amount, alignment, value=None)

Global variable, reserves room in the data area. Has name and size

class ppci.ir.SubRoutine(name)

Base class of function and procedure. These two differ in that a function returns a value, where as a procedure does not.

Design trade-off: In C, a void type is introduced to permit functions that return nothing (void). This seems somewhat artificial, but keeps things simple for the users. In pascal, the procedure and function types are explicit, and the void type is not needed. This is also the approach taken here.

So instead of a Function and Call types, we have Function, Procedure, FunctionCall and ProcedureCall types.

add_block(block)

Add a block to this function

remove_block(block)

Remove a block from this function

class ppci.ir.Procedure(name)

A procedure definition that does not return a value

class ppci.ir.Function(name, return_ty)

Represents a function.

class ppci.ir.Block(name)

Uninterrupted sequence of instructions.

A block is properly terminated if its last instruction is a FinalInstruction.

add_instruction(instruction)

Add an instruction to the end of this block

is_closed

Determine whether this block is propert terminated

is_empty

Determines whether the block is empty or not

predecessors

Return all predecessing blocks

remove_instruction(instruction)

Remove instruction from block

successors

Get the direct successors of this block

Types

Only simple types are available.

ppci.ir.ptr

Pointer type

ppci.ir.i64

Signed 64-bit type

ppci.ir.i32

Signed 32-bit type

ppci.ir.i16

Signed 16-bit type

ppci.ir.i8

Signed 8-bit type

ppci.ir.u64

Unsigned 64-bit type

ppci.ir.u32

Unsigned 32-bit type

ppci.ir.u16

Unsigned 16-bit type

ppci.ir.u8

Unsigned 8-bit type

ppci.ir.f64

64-bit floating point type

ppci.ir.f32

32-bit floating point type

Instructions

The following instructions are available.

Memory instructions

class ppci.ir.Load(address, name, ty, volatile=False)

Load a value from memory

class ppci.ir.Store(value, address, volatile=False)

Store a value into memory

class ppci.ir.Alloc(name, amount, alignment)

Allocates space on the stack. The type of this value is a ptr

Data instructions

class ppci.ir.Const(value, name, ty)

Represents a constant value

class ppci.ir.LiteralData(data, name)

Instruction that contains labeled data. When generating code for this instruction, a label and its data is emitted in the literal area

class ppci.ir.Binop(a, operation, b, name, ty)

Generic binary operation

class ppci.ir.Cast(value, name, ty)

Base type conversion instruction

Control flow instructions

class ppci.ir.ProcedureCall(function_name, arguments)

Call a procedure with some arguments

class ppci.ir.FunctionCall(function_name, arguments, name, ty)

Call a function with some arguments and a return value

class ppci.ir.Jump(target)

Jump statement to another block within the same function

class ppci.ir.CJump(a, cond, b, lab_yes, lab_no)

Conditional jump to true or false labels.

class ppci.ir.Return(result)

This instruction returns a value and exits the function.

class ppci.ir.Exit

Instruction that exits the procedure.

Other

class ppci.ir.Phi(name, ty)

Imaginary phi instruction to make SSA possible.

class ppci.ir.Undefined(name: str, ty: ppci.ir.Typ)

Undefined value, this value must never be used.

Abstract instruction classes

There are some abstract instructions, which cannot be used directly but serve as base classes for other instructions.

class ppci.ir.Instruction

Base class for all instructions that go into a basic block

function

Return the function this instruction is part of

class ppci.ir.Value(name: str, ty: ppci.ir.Typ)

Base of all values

is_used

Determine whether this value is used anywhere

class ppci.ir.FinalInstruction

Final instruction in a basic block

Uml

digraph "classes_foo" {
charset="utf-8"
rankdir=BT
"0" [label="{Alloc|alignment\lamount\l|}", shape="record"];
"1" [label="{BaseFunctionCall|arguments\l|replace_use()\l}", shape="record"];
"2" [label="{BaseProcedureCall|arguments\l|replace_use()\l}", shape="record"];
"3" [label="{BasicTyp|bits\lsize\l|}", shape="record"];
"4" [label="{Binop|a\la : property\lb\lb : property\loperation\lops : list\l|}", shape="record"];
"5" [label="{BlobDataTyp|alignment : int\lsize\l|get()\l}", shape="record"];
"6" [label="{Block|first_instruction\lfunction : NoneType\linstructions : list\lis_closed\lis_empty\lis_entry\lis_used\llast_instruction\lname\lphis\lpredecessors\lreferences : set\lsuccessors\l|add_instruction()\lchange_target()\ldelete()\ldump()\linsert_instruction()\lremove_instruction()\lreplace_incoming()\l}", shape="record"];
"7" [label="{CJump|a\la : property\lb\lb : property\lcond\lconditions : list\llab_no\llab_no : property\llab_yes\llab_yes : property\l|}", shape="record"];
"8" [label="{Cast|src\lsrc : property\l|}", shape="record"];
"9" [label="{Const|value\l|}", shape="record"];
"10" [label="{Exit|targets : list\l|}", shape="record"];
"11" [label="{External|\l|}", shape="record"];
"12" [label="{ExternalFunction|return_type\l|}", shape="record"];
"13" [label="{ExternalProcedure|\l|}", shape="record"];
"14" [label="{ExternalSubRoutine|argument_types\l|}", shape="record"];
"15" [label="{ExternalVariable|\l|}", shape="record"];
"16" [label="{FinalInstruction|\l|}", shape="record"];
"17" [label="{FloatingPointTyp|\l|}", shape="record"];
"18" [label="{Function|return_ty\l|}", shape="record"];
"19" [label="{FunctionCall|function_name\l|}", shape="record"];
"20" [label="{FunctionPointerCall|function_ptr\lfunction_ptr : property\l|}", shape="record"];
"21" [label="{GlobalValue|\l|}", shape="record"];
"22" [label="{Instruction|block : NoneType\lfunction\lis_terminator\lposition\luses : set\l|add_use()\ldel_use()\ldelete()\lremove_from_block()\lreplace_use()\l}", shape="record"];
"23" [label="{IntegerTyp|\l|}", shape="record"];
"24" [label="{Jump|target\ltarget : property\l|}", shape="record"];
"25" [label="{JumpBase|targets\l|change_target()\ldelete()\lset_target_block()\l}", shape="record"];
"26" [label="{LiteralData|data\l|}", shape="record"];
"27" [label="{Load|address\laddress : property\lvolatile : bool\l|}", shape="record"];
"28" [label="{LocalValue|\l|used_in_blocks()\l}", shape="record"];
"29" [label="{Module|debug_db : NoneType\lexternals : list\lfunctions\lname\lvariables\l|add_external()\ladd_function()\ladd_variable()\ldisplay()\lstats()\l}", shape="record"];
"30" [label="{Parameter|\l|}", shape="record"];
"31" [label="{Phi|inputs : dict\l|del_incoming()\lget_value()\lreplace_use()\lset_incoming()\l}", shape="record"];
"32" [label="{PointerTyp|\l|}", shape="record"];
"33" [label="{Procedure|\l|}", shape="record"];
"34" [label="{ProcedureCall|function_name\l|}", shape="record"];
"35" [label="{ProcedurePointerCall|function_ptr\lfunction_ptr : property\l|}", shape="record"];
"36" [label="{Return|result\lresult : property\ltargets : list\l|}", shape="record"];
"37" [label="{SignedIntegerTyp|signed : bool\l|}", shape="record"];
"38" [label="{Store|address\laddress : property\lvalue\lvalue : property\lvolatile : bool\l|}", shape="record"];
"39" [label="{SubRoutine|arguments : list\lblock_names\lblocks : list\ldefined_names : set\lentry : NoneType\llogger : NoneType, RootLogger\lunique_counter : int\l|add_block()\ladd_parameter()\lcalc_reachable_blocks()\ldelete_unreachable()\ldump()\lmake_unique_name()\lnum_instructions()\lremove_block()\l}", shape="record"];
"40" [label="{Typ|is_integer\lname\l|}", shape="record"];
"41" [label="{Undefined|\l|}", shape="record"];
"42" [label="{Unop|a\la : property\loperation\lops : list\l|}", shape="record"];
"43" [label="{UnsignedIntegerTyp|signed : bool\l|}", shape="record"];
"44" [label="{Value|is_used\lname\lty\luse_count\lused_by : set\l|add_user()\ldel_user()\lreplace_by()\l}", shape="record"];
"45" [label="{Variable|alignment\lamount\lvalue : NoneType\l|}", shape="record"];
"0" -> "28" [arrowhead="empty", arrowtail="none"];
"1" -> "28" [arrowhead="empty", arrowtail="none"];
"2" -> "22" [arrowhead="empty", arrowtail="none"];
"3" -> "40" [arrowhead="empty", arrowtail="none"];
"4" -> "28" [arrowhead="empty", arrowtail="none"];
"5" -> "40" [arrowhead="empty", arrowtail="none"];
"7" -> "25" [arrowhead="empty", arrowtail="none"];
"8" -> "28" [arrowhead="empty", arrowtail="none"];
"9" -> "28" [arrowhead="empty", arrowtail="none"];
"10" -> "16" [arrowhead="empty", arrowtail="none"];
"11" -> "21" [arrowhead="empty", arrowtail="none"];
"12" -> "14" [arrowhead="empty", arrowtail="none"];
"13" -> "14" [arrowhead="empty", arrowtail="none"];
"14" -> "11" [arrowhead="empty", arrowtail="none"];
"15" -> "11" [arrowhead="empty", arrowtail="none"];
"16" -> "22" [arrowhead="empty", arrowtail="none"];
"17" -> "3" [arrowhead="empty", arrowtail="none"];
"18" -> "39" [arrowhead="empty", arrowtail="none"];
"19" -> "1" [arrowhead="empty", arrowtail="none"];
"20" -> "1" [arrowhead="empty", arrowtail="none"];
"21" -> "44" [arrowhead="empty", arrowtail="none"];
"23" -> "3" [arrowhead="empty", arrowtail="none"];
"24" -> "25" [arrowhead="empty", arrowtail="none"];
"25" -> "16" [arrowhead="empty", arrowtail="none"];
"26" -> "28" [arrowhead="empty", arrowtail="none"];
"27" -> "28" [arrowhead="empty", arrowtail="none"];
"28" -> "22" [arrowhead="empty", arrowtail="none"];
"28" -> "44" [arrowhead="empty", arrowtail="none"];
"30" -> "28" [arrowhead="empty", arrowtail="none"];
"31" -> "28" [arrowhead="empty", arrowtail="none"];
"32" -> "40" [arrowhead="empty", arrowtail="none"];
"33" -> "39" [arrowhead="empty", arrowtail="none"];
"34" -> "2" [arrowhead="empty", arrowtail="none"];
"35" -> "2" [arrowhead="empty", arrowtail="none"];
"36" -> "16" [arrowhead="empty", arrowtail="none"];
"37" -> "23" [arrowhead="empty", arrowtail="none"];
"38" -> "22" [arrowhead="empty", arrowtail="none"];
"39" -> "21" [arrowhead="empty", arrowtail="none"];
"41" -> "28" [arrowhead="empty", arrowtail="none"];
"42" -> "28" [arrowhead="empty", arrowtail="none"];
"43" -> "23" [arrowhead="empty", arrowtail="none"];
"45" -> "21" [arrowhead="empty", arrowtail="none"];
}