def argument_address(func_type, cpu, mem): index = size(void_pointer_type) + (size(void_pointer_type) if size( c_type(c_type(func_type)), overrides={VoidType: 0}) else 0) for ctype in imap(c_type, func_type): yield cpu.base_pointer + index index += size(ctype)
def compound_assignment(expr, symbol_table): assert all(imap(isinstance, imap(c_type, (left_exp(expr), right_exp(expr))), repeat(NumericType))) assert not isinstance(c_type(left_exp(expr)), ArrayType) if isinstance(left_exp(expr), IdentifierExpression) and \ base_c_type(c_type(left_exp(expr))) == base_c_type(c_type(right_exp(expr))) and \ size(c_type(left_exp(expr))) == size(c_type(right_exp(expr))): # check that both operands are of the same kind (integral vs numeric) and have the same size ... return simple_numeric_assignment_no_casting(expr, symbol_table, rules(compound_assignment)[oper(expr)]) max_type = max(imap(c_type, (left_exp(expr), right_exp(expr)))) # cast to largest type. expression = symbol_table['__ expression __'] left_instrs = cast( # cast to max_type patch_comp_left_instrs(expression(left_exp(expr), symbol_table), loc(expr), size(c_type(left_exp(expr)))), c_type(left_exp(expr)), max_type, loc(expr), ) right_instrs = cast(expression(right_exp(expr), symbol_table), c_type(right_exp(expr)), max_type, loc(expr)) return patch_comp_assignment( cast( # Cast the result back, swap the value and the destination address call set to save. rules(compound_assignment)[oper(expr)]( left_instrs, right_instrs, loc(expr), (max_type, c_type(left_exp(expr)), c_type(right_exp(expr))) ), max_type, c_type(expr), loc(expr) ), c_type(expr), loc(expr), )
def argument_address(func_type, cpu, mem): index = size(void_pointer_type) + ( size(void_pointer_type) if size(c_type(c_type(func_type)), overrides={VoidType: 0}) else 0 ) for ctype in imap(c_type, func_type): yield cpu.base_pointer + index index += size(ctype)
def element_selection(expr, symbol_table): instrs = symbol_table['__ expression __'](left_exp(expr), symbol_table) # if we are loading the structure then just remove the Load instr, calculate the elements offset and Load that elem if is_load(instrs): return element_instrs(c_type(left_exp(expr)), right_exp(expr), loc(expr), load_instrs=all_but_last(instrs, Loads, loc(expr))) struct_size, member_size = size(c_type( left_exp(expr))), size_arrays_as_pointers(c_type(expr)) addr_instrs = add( # calculate the loaded structured members address load_stack_pointer(loc(expr)), push(struct_member_offset(c_type(left_exp(expr)), right_exp(expr)), loc(expr)), loc(expr)) return chain( # copy the element then move it to the base of the structure and deallocate everything else set_instr( chain( # load the value in question if its not an array (which is just an address ...) (addr_instrs if isinstance(c_type(expr), ArrayType) else load( addr_instrs, member_size, loc(expr))), add(load_stack_pointer(loc(expr)), push((struct_size + member_size), loc(expr)), loc(expr)), ), member_size, loc(expr)), allocate(-(struct_size - member_size), loc( expr)) # deallocate structure and copied member (set leaves value) )
def element_selection(expr, symbol_table): instrs = symbol_table['__ expression __'](left_exp(expr), symbol_table) # if we are loading the structure then just remove the Load instr, calculate the elements offset and Load that elem if is_load(instrs): return element_instrs( c_type(left_exp(expr)), right_exp(expr), loc(expr), load_instrs=all_but_last(instrs, Loads, loc(expr)) ) struct_size, member_size = size(c_type(left_exp(expr))), size_arrays_as_pointers(c_type(expr)) addr_instrs = add( # calculate the loaded structured members address load_stack_pointer(loc(expr)), push(struct_member_offset(c_type(left_exp(expr)), right_exp(expr)), loc(expr)), loc(expr) ) return chain( # copy the element then move it to the base of the structure and deallocate everything else set_instr( chain( # load the value in question if its not an array (which is just an address ...) (addr_instrs if isinstance(c_type(expr), ArrayType) else load(addr_instrs, member_size, loc(expr))), add(load_stack_pointer(loc(expr)), push((struct_size + member_size), loc(expr)), loc(expr)), ), member_size, loc(expr) ), allocate(-(struct_size - member_size), loc(expr)) # deallocate structure and copied member (set leaves value) )
def definition(dec, symbol_table): # Global definition. assert not isinstance(c_type(dec), FunctionType) symbol_table[name(dec)] = bind_load_instructions(dec) symbol_table[name(dec)].symbol = Data( # Add reference of symbol to definition to keep track of references # static binaries, (packed binaries since machine may require alignment ...) name(dec), static_def_binaries(dec), size(c_type(dec)), dec.storage_class, loc(dec), ) return symbol_table[name(dec)].symbol
def declaration(stmnt, symbol_table): # This are non-global declarations they don't require any space # but they could be referenced (extern, or function type) symbol_type = Code if isinstance(c_type(stmnt), FunctionType) else Data stmnt.symbol = symbol_type(declarations.name(stmnt), (), None, stmnt.storage_class, loc(stmnt)) stmnt.symbol.size = size(c_type(stmnt), overrides={FunctionType: None}) symbol_table[declarations.name(stmnt)] = stmnt yield Pass(loc(stmnt))
def statement(stmnt, symbol_table): is_expression = isinstance(stmnt, expressions.Expression) # Set entry point to False if its an expression or use statement function if present otherwise None. instrs = rules(statement)[type(stmnt)](stmnt, symbol_table) # Almost all Expression statements leave a value on the stack, so we must remove it. if stmnt and is_expression: instrs = chain(instrs, allocate(-size(c_type(stmnt), overrides={VoidType: 0}), loc(stmnt))) return instrs
def executable(symbols, symbol_table=None, entry_point=default_entry_point, libraries=(), linker=static): location = '__SOP__' # Start of Program symbol_table = SymbolTable() if symbol_table is None else symbol_table __end__ = Word(0, location) libs = tuple(imap(Library, libraries)) symbols = chain( symbols, # add heap pointer(s) ... ( object_file.Data('__base_heap_ptr__', (Address(__end__, location), ), size(void_pointer_type), None, location), object_file.Data('__heap_ptr__', (Address(__end__, location), ), size(void_pointer_type), None, location), )) def declarations(symbol_table): # iterate over all symbols withing symbol_table that do not have binaries (they should be declarations) for v in chain.from_iterable( imap( set_binaries, ifilterfalse(lambda s: s.binaries, symbol_table.itervalues()))): yield v # emit default binaries for declarations ... # inject declaration into temp symbol_table to generate entry point function call instructions ... st = {'__ expression __': expression} _ = declaration(entry_point, st) instr_seq = chain( statement( # call entry point ... FunctionCallExpression( IdentifierExpression(name(entry_point), c_type(entry_point), location), (), c_type(c_type(entry_point)), location), st), halt(location), # Halt machine on return ... chain.from_iterable( starmap(binaries, izip(symbols, repeat(symbol_table)))), ) # link all foreign symbols and emit binaries for declarations ... return chain(linker(instr_seq, symbol_table, libs), declarations(symbol_table), (__end__, ))
def __return__(value, cpu, mem, os, func_signature=FunctionType(IntegerType(SysCallLocation), (), SysCallLocation)): cpu.instr_pointer = mem[cpu.base_pointer + word_size] # get return instruction ... assert size(c_type(c_type(func_signature))) == word_size mem[cpu.base_pointer + 2 * word_size] = value
def inc_dec(expr, symbol_table): assert not isinstance(c_type(expr), ArrayType) and isinstance( c_type(expr), IntegralType) value = rules(inc_dec)[type(expr)] if isinstance(c_type(expr), PointerType): value *= size_extended(c_type(c_type(expr))) return get_postfix_update(size(c_type(expr)))(all_but_last( symbol_table['__ expression __'](exp(expr), symbol_table), Loads, loc(expr)), value, loc(expr))
def subtract(l_instrs, r_instrs, location, operand_types): exp_c_type, left_exp_c_type, right_exp_c_type = operand_types if all(imap(isinstance, operand_types[1:], repeat(PointerType))): # subtracting two pointers ... return divide( subtract.rules[base_c_type(void_pointer_type)][size(void_pointer_type)](l_instrs, r_instrs, location), push(size_extended(c_type(c_type(left_exp_c_type))), location), location, operand_types ) return pointer_arithmetic(l_instrs, r_instrs, location, operand_types)
def inc_dec(expr, symbol_table): assert not isinstance(c_type(expr), ArrayType) and isinstance(c_type(expr), IntegralType) value = rules(inc_dec)[type(expr)] if isinstance(c_type(expr), PointerType): value *= size_extended(c_type(c_type(expr))) return get_postfix_update(size(c_type(expr)))( all_but_last(symbol_table['__ expression __'](exp(expr), symbol_table), Loads, loc(expr)), value, loc(expr) )
def pop_frame(location=LocationNotSet, total_size_of_arguments=0, omit_pointer_for_return_value=False): # return pop_frame_instr(location) # method 1 requires special instruction that has to manage blocks # method 2 (5 instructions LoadBaseStackPtr, Push, Add, SetStackPtr, SetBaseStackPtr) # method 2 seems to be faster even though it has an extra instruction compare to method 3, # not really sure why ... probably because the optimizer is removing some of them return set_base_stack_pointer( set_stack_pointer( add( # remove parameters and ret addr ... load_base_stack_pointer(location), push( # remove parameters, ret address pointer, and ptr for ret value if it was emitted ... sum((total_size_of_arguments, size(void_pointer_type), (not omit_pointer_for_return_value) * size(void_pointer_type))), location), location), location), location)
def executable(symbols, symbol_table=None, entry_point=default_entry_point, libraries=(), linker=static): location = '__SOP__' # Start of Program symbol_table = SymbolTable() if symbol_table is None else symbol_table __end__ = Word(0, location) libs = tuple(imap(Library, libraries)) symbols = chain( symbols, # add heap pointer(s) ... ( object_file.Data( '__base_heap_ptr__', (Address(__end__, location),), size(void_pointer_type), None, location ), object_file.Data( '__heap_ptr__', (Address(__end__, location),), size(void_pointer_type), None, location ), ) ) def declarations(symbol_table): # iterate over all symbols withing symbol_table that do not have binaries (they should be declarations) for v in chain.from_iterable(imap(set_binaries, ifilterfalse(lambda s: s.binaries, symbol_table.itervalues()))): yield v # emit default binaries for declarations ... # inject declaration into temp symbol_table to generate entry point function call instructions ... st = {'__ expression __': expression} _ = declaration(entry_point, st) instr_seq = chain( statement( # call entry point ... FunctionCallExpression( IdentifierExpression(name(entry_point), c_type(entry_point), location), (), c_type(c_type(entry_point)), location ), st ), halt(location), # Halt machine on return ... chain.from_iterable(starmap(binaries, izip(symbols, repeat(symbol_table)))), ) # link all foreign symbols and emit binaries for declarations ... return chain(linker(instr_seq, symbol_table, libs), declarations(symbol_table), (__end__,))
def return_statement(stmnt, symbol_table): # TODO: check if we can omit the setting the return value if if it is immediately removed ... return_type = c_type(c_type(symbol_table['__ CURRENT FUNCTION __'])) assert not isinstance(c_type(return_type), ArrayType) if isinstance(return_type, VoidType) or not exp(stmnt): # just return if void type or expr is empty or size of expression is zero. return return_instrs(loc(stmnt)) return chain( cast(symbol_table['__ expression __'](exp(stmnt), symbol_table), c_type(exp(stmnt)), return_type, loc(stmnt)), set_instr( load( add(load_base_stack_pointer(loc(stmnt)), push(size(void_pointer_type), loc(stmnt)), loc(stmnt)), size(void_pointer_type), loc(stmnt) ), size(return_type), loc(stmnt) ), # TODO: see if we can remove the following instr, since pop_frame will reset the base and stack pointers # allocate(-size(return_type), loc(stmnt)), # Set leaves the value on the stack return_instrs(loc(stmnt)) )
def push_frame( arguments_instrs=(), location=LocationNotSet, total_size_of_arguments=0, omit_pointer_for_return_value=False, ): return chain( load_base_stack_pointer(location), # save previous base stack pointer ... arguments_instrs, # Pointer to where to store return values, if applicable (ie non-zero return size)... () if omit_pointer_for_return_value else # calculate pointer for ret value, excluding base pointer ... add(load_stack_pointer(location), push((total_size_of_arguments + size(void_pointer_type)), location), location) )
def definition(dec, symbol_table): # Global definition. assert not isinstance(c_type(dec), FunctionType) symbol_table[name(dec)] = bind_load_instructions(dec) symbol_table[name( dec )].symbol = Data( # Add reference of symbol to definition to keep track of references # static binaries, (packed binaries since machine may require alignment ...) name(dec), static_def_binaries(dec), size(c_type(dec)), dec.storage_class, loc(dec), ) return symbol_table[name(dec)].symbol
def test_printf_union(self): code = """ #include <stdio.h> union {unsigned long long a; double b; char c[20]; int d[0];} foo = {.a=10, .b=10.5}; int main() { printf("%llu %f %lu", foo.a, foo.b, sizeof(foo)); return foo.b == 10.5; } """ self.evaluate(code) self.assertEqual("4622100592565682176 10.5 {s}".format(s=size(ArrayType(CharType(), 20))), self.stdout.read())
def pop_frame(location=LocationNotSet, total_size_of_arguments=0, omit_pointer_for_return_value=False): # return pop_frame_instr(location) # method 1 requires special instruction that has to manage blocks # method 2 (5 instructions LoadBaseStackPtr, Push, Add, SetStackPtr, SetBaseStackPtr) # method 2 seems to be faster even though it has an extra instruction compare to method 3, # not really sure why ... probably because the optimizer is removing some of them return set_base_stack_pointer( set_stack_pointer( add( # remove parameters and ret addr ... load_base_stack_pointer(location), push( # remove parameters, ret address pointer, and ptr for ret value if it was emitted ... sum(( total_size_of_arguments, size(void_pointer_type), (not omit_pointer_for_return_value) * size(void_pointer_type))), location ), location ), location ), location )
def call_function(function_call_expr, symbol_table): l, expr = loc(function_call_expr), left_exp(function_call_expr) return chain( # if expression is a simple identifier of function type, no need for AbsoluteJump, use RelativeJump set_base_stack_pointer(load_stack_pointer(l), l), relative_jump(Offset(symbol_table[name(expr)].get_address_obj(l).obj, l), l), ) if isinstance(expr, IdentifierExpression) and isinstance(c_type(expr), FunctionType) else absolute_jump( chain( symbol_table['__ expression __'](expr, symbol_table), # load callee address # calculate new base stack pointer excluding the callees address ... # give the callee a new frame... if we where to reset the base stack ptr before evaluating the left_expr # we run the risk of failing to properly load function address if it was store as a local function pointer set_base_stack_pointer(add(load_stack_pointer(l), push(size(void_pointer_type), l), l), l) ), l )
def test_printf_union(self): code = """ #include <stdio.h> union {unsigned long long a; double b; char c[20]; int d[0];} foo = {.a=10, .b=10.5}; int main() { printf("%llu %f %lu", foo.a, foo.b, sizeof(foo)); return foo.b == 10.5; } """ self.evaluate(code) self.assertEqual( "4622100592565682176 10.5 {s}".format( s=size(ArrayType(CharType(), 20))), self.stdout.read())
def array_subscript(expr, symbol_table): assert isinstance(c_type(left_exp(expr)), PointerType) and isinstance( c_type(right_exp(expr)), IntegralType) expression = symbol_table['__ expression __'] l = loc(expr) addr_instrs = add( expression(left_exp(expr), symbol_table), multiply( cast( # convert right expression to address type in order to properly multiply ... expression(right_exp(expr), symbol_table), c_type(right_exp(expr)), void_pointer_type, l), push(size(c_type(expr)), l), l), l, ) # Load value unless the return type is also an ArrayTyp return addr_instrs if isinstance(c_type(expr), ArrayType) \ else load(addr_instrs, size_arrays_as_pointers(c_type(expr)), l)
def push_frame( arguments_instrs=(), location=LocationNotSet, total_size_of_arguments=0, omit_pointer_for_return_value=False, ): return chain( load_base_stack_pointer( location), # save previous base stack pointer ... arguments_instrs, # Pointer to where to store return values, if applicable (ie non-zero return size)... () if omit_pointer_for_return_value else # calculate pointer for ret value, excluding base pointer ... add( load_stack_pointer(location), push((total_size_of_arguments + size(void_pointer_type)), location), location))
def call_function(function_call_expr, symbol_table): l, expr = loc(function_call_expr), left_exp(function_call_expr) return chain( # if expression is a simple identifier of function type, no need for AbsoluteJump, use RelativeJump set_base_stack_pointer(load_stack_pointer(l), l), relative_jump( Offset(symbol_table[name(expr)].get_address_obj(l).obj, l), l), ) if isinstance(expr, IdentifierExpression) and isinstance( c_type(expr), FunctionType ) else absolute_jump( chain( symbol_table['__ expression __']( expr, symbol_table), # load callee address # calculate new base stack pointer excluding the callees address ... # give the callee a new frame... if we where to reset the base stack ptr before evaluating the left_expr # we run the risk of failing to properly load function address if it was store as a local function pointer set_base_stack_pointer( add(load_stack_pointer(l), push(size(void_pointer_type), l), l), l)), l)
def simple_numeric_assignment_no_casting(expr, symbol_table, operation): expression = symbol_table['__ expression __'] # used when the left operand is an identifier so we can re-emit binaries instead of using expensive Dup instr return set_instr( chain( operation( expression(left_exp(expr), symbol_table), expression(right_exp(expr), symbol_table), loc(expr), tuple(imap(c_type, (expr, left_exp(expr), right_exp(expr)))) ), expression( AddressOfExpression(left_exp(expr), PointerType(c_type(right_exp(expr)), loc(expr)), loc(expr)), symbol_table, ) ), size(c_type(expr)), loc(expr), )
def array_subscript(expr, symbol_table): assert isinstance(c_type(left_exp(expr)), PointerType) and isinstance(c_type(right_exp(expr)), IntegralType) expression = symbol_table['__ expression __'] l = loc(expr) addr_instrs = add( expression(left_exp(expr), symbol_table), multiply( cast( # convert right expression to address type in order to properly multiply ... expression(right_exp(expr), symbol_table), c_type(right_exp(expr)), void_pointer_type, l ), push(size(c_type(expr)), l), l ), l, ) # Load value unless the return type is also an ArrayTyp return addr_instrs if isinstance(c_type(expr), ArrayType) \ else load(addr_instrs, size_arrays_as_pointers(c_type(expr)), l)
def __exit__( cpu, mem, kernel, func_signature=FunctionType( VoidType(SysCallLocation), (AbstractDeclarator(IntegerType(SysCallLocation), SysCallLocation),), SysCallLocation )): # void exit(int return_value); value, = args(func_signature, cpu, mem) # Flush and close all opened files except stdio for file_id in ifilter(lambda file_id: file_id not in dict(std_files), kernel.opened_files): kernel.opened_files[file_id].flush() kernel.opened_files[file_id].close() mem[-size(IntegerType())] = value # Set the return status on top of the stack cpu.base_pointer = cpu.stack_pointer = push_integral.core_type(-word_size) # reset stack/base pointers ... mem[cpu.instr_pointer + word_size] = Halt(SysCallLocation) # Halt machine ...
def function_call(expr, symbol_table): assert not isinstance(c_type(expr), ArrayType) l, return_instr = loc(expr), Pass(loc(expr)) total_size_of_arguments = sum(imap(size_arrays_as_pointers, imap(c_type, right_exp(expr)))) return_size = size(c_type(expr), overrides={VoidType: 0}) omit_pointer_for_return_value = not return_size # if omit_pointer_for_return_value: # # if the function call is a statement or its return type has zero size. # # lets do some minor optimizations, since function calls already quite expensive as it is. # expr.c_type = VoidType(loc(l)) # change the return type of the expression to void. # # since we are omitting the return pointer, we have to update the parameter offsets, since they are calculated # # assuming that a return pointer is added ... # # all sets should be decrease by size(void_pointer) for this expression but this may not be true for other # # function call expressions that may actually use the return value of this as such ... :( # we would need to check if the functions return value is ALWAYS ignored if and only if then can we omit # the return pointer for know its only applicable for for functions whose return size is zero ... # TODO: post-compilation optimization that optimizes functions whose returned values is ALWAYS ignored. expression = symbol_table['__ expression __'] return chain( # Allocate space for return value, save frame. allocate(return_size, l), push_frame( # Push arguments in reverse order (right to left) ... chain.from_iterable(imap(expression, reverse(right_exp(expr)), repeat(symbol_table))), location=l, total_size_of_arguments=total_size_of_arguments, omit_pointer_for_return_value=omit_pointer_for_return_value ), push(Address(return_instr, l), l), # make callee aware of were to return to. call_function(expr, symbol_table), (return_instr,), # Pop Frame, first stack pointer then base stack pointer pop_frame( location=l, total_size_of_arguments=total_size_of_arguments, omit_pointer_for_return_value=omit_pointer_for_return_value ) )
def __exit__( cpu, mem, kernel, func_signature=FunctionType(VoidType(SysCallLocation), (AbstractDeclarator( IntegerType(SysCallLocation), SysCallLocation), ), SysCallLocation)): # void exit(int return_value); value, = args(func_signature, cpu, mem) # Flush and close all opened files except stdio for file_id in ifilter(lambda file_id: file_id not in dict(std_files), kernel.opened_files): kernel.opened_files[file_id].flush() kernel.opened_files[file_id].close() mem[-size(IntegerType() )] = value # Set the return status on top of the stack cpu.base_pointer = cpu.stack_pointer = push_integral.core_type( -word_size) # reset stack/base pointers ... mem[cpu.instr_pointer + word_size] = Halt( SysCallLocation) # Halt machine ...
def function_call(expr, symbol_table): assert not isinstance(c_type(expr), ArrayType) l, return_instr = loc(expr), Pass(loc(expr)) total_size_of_arguments = sum( imap(size_arrays_as_pointers, imap(c_type, right_exp(expr)))) return_size = size(c_type(expr), overrides={VoidType: 0}) omit_pointer_for_return_value = not return_size # if omit_pointer_for_return_value: # # if the function call is a statement or its return type has zero size. # # lets do some minor optimizations, since function calls already quite expensive as it is. # expr.c_type = VoidType(loc(l)) # change the return type of the expression to void. # # since we are omitting the return pointer, we have to update the parameter offsets, since they are calculated # # assuming that a return pointer is added ... # # all sets should be decrease by size(void_pointer) for this expression but this may not be true for other # # function call expressions that may actually use the return value of this as such ... :( # we would need to check if the functions return value is ALWAYS ignored if and only if then can we omit # the return pointer for know its only applicable for for functions whose return size is zero ... # TODO: post-compilation optimization that optimizes functions whose returned values is ALWAYS ignored. expression = symbol_table['__ expression __'] return chain( # Allocate space for return value, save frame. allocate(return_size, l), push_frame( # Push arguments in reverse order (right to left) ... chain.from_iterable( imap(expression, reverse(right_exp(expr)), repeat(symbol_table))), location=l, total_size_of_arguments=total_size_of_arguments, omit_pointer_for_return_value=omit_pointer_for_return_value), push(Address(return_instr, l), l), # make callee aware of were to return to. call_function(expr, symbol_table), (return_instr, ), # Pop Frame, first stack pointer then base stack pointer pop_frame(location=l, total_size_of_arguments=total_size_of_arguments, omit_pointer_for_return_value=omit_pointer_for_return_value))
def patch_comp_left_instrs(instrs, location, value_size): return load( # duplicate address ... chain(all_but_last(instrs, Loads, location), dup(size(void_pointer_type), location)), value_size, location )
def const_float_expr(expr): return get_push(size(c_type(expr)))(DoubleHalf(exp(expr), loc(expr)), loc(expr))
def patch_comp_assignment(instrs, expr_type, location): # At this point the stack contains the Address followed by the calculated value ... # we need to swap them and call set, but we mut be careful that both have the same size before calling swap # or we could corrupt the value ... if size(expr_type) == size(void_pointer_type): # result type and pointer type (address) equal no alignment required return chain(instrs, set_instr(swap(size(void_pointer_type), location), size(void_pointer_type), location)) if size(expr_type) < size(void_pointer_type): # size of result type is less than address we need to align if isinstance(expr_type, DoubleType): # since we are using cast to the alignment we need too interpret assert size(double_type) == size(long_type) # result type as integral type for casting may change value expr_type = LongType(location, unsigned=True) elif isinstance(expr_type, FloatType): assert size(float_type) == size(integer_type) expr_type = IntegerType(location, unsigned=True) return cast( # convert the value back to its original size removing any alignment added bytes after set ... set_instr( # set values assuming little endian architecture TODO: check on that assertion! chain(cast(instrs, expr_type, void_pointer_type, location), swap(size(void_pointer_type), location)), size(expr_type), # if sizes differ, cast value to pointer type extending bytes, and swap location ), void_pointer_type, expr_type, location ) else: raise ValueError('{l} unable to patch compound assignment value size {s} exceeds address size {a}'.format( l=location, s=size(expr_type), a=size(void_pointer_type) ))
def args(func_type, cpu, mem): for address, arg in izip(argument_address(func_type, cpu, mem), func_type): yield mem[address] \ if size(c_type(arg)) == word_size \ else (mem[offset] for offset in xrange(address, address + size(c_type(arg)), word_size))
def const_double_expr(expr): return get_push(size(c_type(expr)))(Double(exp(expr), loc(expr)), loc(expr))
def stack_allocation(stack, obj): obj_type = obj if isinstance(obj, CType) else c_type(obj) stack.allocate(size(obj_type)) return bind_instructions(obj, stack.stack_pointer) if isinstance( obj, (Declaration, Declarator)) else obj
def static_integral_ctype(ctype, value=0, location=LocationNotSet): yield machine_integral_types[size(ctype)](value, location or loc(ctype))
def static_real_ctype(ctype, value=0.0, location=LocationNotSet): yield machine_floating_types[size(ctype)](value, location or loc(ctype))
def static_union_exp(expr): return chain( imap(binaries, exp(expr)[0]), imap(Byte, repeat(0, size(c_type(expr)) - size(c_type(exp(expr)[0])))) )
chain( expression(e, symbol_table), allocate(-size_arrays_as_pointers(c_type(e), overrides={VoidType: 0}), loc(e)) ) for e in exp(expr)[:-1] ), expression(exp(expr)[-1], symbol_table) ) # Entry point to all expression or expression statements def expression(expr, symbol_table): return rules(expression)[type(expr)](expr, symbol_table) expression_funcs = unary_expression, postfix_expression, ternary_expression, initializer_expression set_rules( expression, chain( ( (EmptyExpression, lambda expr, *_: allocate(size(c_type(expr), overrides={VoidType: 0}), loc(expr))), (ConstantExpression, constant_expression), (CastExpression, cast_expression), (IdentifierExpression, identifier_expression), (CommaExpression, comma_expression), (BinaryExpression, binary_expression), (AssignmentExpression, binary_expression), (CompoundAssignmentExpression, binary_expression), ), chain.from_iterable(imap(izip, imap(rules, expression_funcs), imap(repeat, expression_funcs))) ) )
chain( expression(e, symbol_table), allocate( -size_arrays_as_pointers(c_type(e), overrides={VoidType: 0}), loc(e))) for e in exp(expr)[:-1]), expression(exp(expr)[-1], symbol_table)) # Entry point to all expression or expression statements def expression(expr, symbol_table): return rules(expression)[type(expr)](expr, symbol_table) expression_funcs = unary_expression, postfix_expression, ternary_expression, initializer_expression set_rules( expression, chain(( (EmptyExpression, lambda expr, *_: allocate( size(c_type(expr), overrides={VoidType: 0}), loc(expr))), (ConstantExpression, constant_expression), (CastExpression, cast_expression), (IdentifierExpression, identifier_expression), (CommaExpression, comma_expression), (BinaryExpression, binary_expression), (AssignmentExpression, binary_expression), (CompoundAssignmentExpression, binary_expression), ), chain.from_iterable( imap(izip, imap(rules, expression_funcs), imap(repeat, expression_funcs)))))
def union_initializer(expr, symbol_table): return chain( allocate(size(c_type(expr)) - size(c_type(expr[0])), loc(expr)), numeric_initializer(expr, symbol_table) )
def const_integral_expr(expr): return get_push(size(c_type(expr)))(exp(expr), loc(expr))
def size_of(expr, symbol_table): ctype = exp(expr) if isinstance(exp(expr), CType) else c_type(exp(expr)) return symbol_table['__ expression __'](ConstantExpression(size(ctype), c_type(expr), loc(expr)), symbol_table)
def const_union_expr(expr): assert len(exp(expr)) <= 1 return chain( # allocate rest ... allocate( size(c_type(expr)) - sum(imap(size, imap(c_type, exp(expr)))), loc(expr)), const_struct_expr(exp(expr)))
def stack_de_allocation(stack, obj): obj_type = obj if isinstance(obj, CType) else c_type(obj) stack.de_allocate(size(obj)) return unbind_instructions(stack, obj) if isinstance( obj, (Declaration, Declarator)) else obj
def declaration(dec, symbol_table): symbol_table[name(dec)] = bind_load_instructions(dec) symbol_table[name(dec)].symbol = Code(name(dec), (), None, dec.storage_class, loc(dec)) \ if isinstance(c_type(dec), FunctionType) \ else Data(name(dec), (), size(c_type(dec)), dec.storage_class, loc(dec)) return symbol_table[name(dec)].symbol
def const_union_expr(expr): assert len(exp(expr)) <= 1 return chain( # allocate rest ... allocate(size(c_type(expr)) - sum(imap(size, imap(c_type, exp(expr)))), loc(expr)), const_struct_expr(exp(expr)) )
def const_double_expr(expr): return get_push(size(c_type(expr)))(Double(exp(expr), loc(expr)), loc(expr))
def args(func_type, cpu, mem): for address, arg in izip(argument_address(func_type, cpu, mem), func_type): yield mem[address] \ if size(c_type(arg)) == word_size \ else (mem[offset] for offset in xrange(address, address + size(c_type(arg)), word_size))
def __return__(value, cpu, mem, os, func_signature=FunctionType(IntegerType(SysCallLocation), (), SysCallLocation)): cpu.instr_pointer = mem[cpu.base_pointer + word_size] # get return instruction ... assert size(c_type(c_type(func_signature))) == word_size mem[cpu.base_pointer + 2 * word_size] = value