-- cg.e -- Code Generation module for yet another mini-language -- Pete Eberlein -- 13 Sep 2002 global constant ASSIGN=1, POKE4=2, PEEK4=3, ADDTO=4, CALLPROC=5, CALLFUNC=6, RETURN=7, WHILE=8, IF=9, CONST=10, VAR=11, EQUALS=12, NOT_EQUALS=13, LESS_THAN=14, GREATER_THAN=15, LESS_EQUALS=16, GREATER_EQUALS=17, AND=18, OR=19, XOR=20, EXIT=21, CAT=22, ADD=23, SUB=24, MUL=25, DIV=26, AND_BITS=27, OR_BITS=28, XOR_BITS=30, NOT_BITS=31, NOT=32, NEG=33 include machine.e include misc.e -- calling_convention: 0=Linux, 1=Windows constant calling_convention = (platform() = WIN32) include as.e -- assembler sequence locals, return_code function find_var(object name) integer idx if atom(name) then return {name} end if idx = find(name, locals) if idx then return {esp,4*(idx-1)} end if printf(2, "Could not find var %s\n", {name}) end function function Push(sequence code) -- returns whatever on stack if find("mov",code[length(code)])=1 and find(eax,code[length(code)])=2 then code[length(code)] = {"push",code[length(code)][3]} else code &= {{"push", eax}} end if locals = prepend(locals,0) return code end function function expr_gen(object st) -- returns whatever in eax sequence result,op if st[1] = VAR then result = {{"mov",eax,find_var(st[2])}} elsif st[1] = CONST then result = {{"mov",eax,st[2]}} elsif st[1] = ADD or st[1] = SUB or st[1] = AND_BITS or st[1] = OR_BITS or st[1] = XOR_BITS then op = {"add","sub","And","Or","Xor"} op = op[find(st[1],{ADD,SUB,AND_BITS,OR_BITS,XOR_BITS})] result = expr_gen(st[2]) if st[3][1] = VAR then result &= {{op,eax,find_var(st[3][2])}} elsif st[3][1] = CONST then result &= {{op,eax,st[3][2]}} else result &= {{"push",eax}} locals = prepend(locals,0) result &= expr_gen(st[3]) locals = locals[2..length(locals)] result &= {{op,eax,{esp}},{"add",esp,4}} end if elsif st[1] = NOT_BITS then result = expr_gen(st[2]) & {{"Not",eax}} elsif st[1] = NEG then result = expr_gen(st[2]) & {{"neg",eax}} elsif st[1] = PEEK4 then if st[2][1] = CONST then result &= {{"mov",eax, {st[2][2]}}} else result &= expr_gen(st[2]) & {{"mov",eax,{eax}}} end if elsif st[1] = CALLPROC or st[1] = CALLFUNC then for a=length(st) to 3 by -1 do result &= Push(expr_gen(st[a])) end for locals = locals[length(st)-1..length(locals)] result &= {{"mov",eax,st[2]},{"call_near",eax}} if calling_convention = 0 and length(st) > 2 then result &= {{"add",esp,4*(length(st)-2)}} end if else puts(2, "error\n") end if return result end function function branch_gen(sequence st) sequence result if st[2][1] = VAR then result = {{"cmp",find_var(st[2][2]), 0}} elsif st[2][1] = CONST then result = {{"mov",eax,st[2][2]},{"cmp",eax, 0}} elsif st[2][2][1] = VAR and st[2][3][1] = CONST then result = {{"cmp",find_var(st[2][2][2]), st[2][3][2]}} elsif st[2][2][1] = VAR and st[2][3][1] = VAR then result = {{"mov",eax,find_var(st[2][3][2])},{"cmp",find_var(st[2][2][2]),eax}} elsif st[2][2][1] = CONST and st[2][3][1] = VAR then result = {{"mov",eax,st[2][3][2]},{"cmp",eax,find_var(st[2][2][2])}} elsif st[2][2][1] = CONST and st[2][3][1] = CONST then result = {{"mov",eax,st[2][3][2]},{"cmp",eax,st[2][2][2]}} else puts(2, "error\n") end if return result end function function jump_gen(sequence st, integer offset) sequence jump_table jump_table = {"je","jne","jnge","jnl","jng","jnle","je","je"} return {{jump_table[find(st[2][1],{NOT_EQUALS,EQUALS,GREATER_EQUALS, LESS_THAN,GREATER_THAN,LESS_EQUALS,VAR,CONST})], offset}} end function function exit_gen(sequence code) integer i i = find({"exit"}, code) while i do code = code[1..i-1] & {{"jmp", 2+length(code)-i}} & code[i+1..length(code)] i = find({"exit"}, code) end while return code end function function code_gen(sequence code) sequence result, st, tmp result = {} for i = 1 to length(code) do st = code[i] if st[1] = ASSIGN then if st[3][1] = CONST then result &= {{"mov",find_var(st[2]), st[3][2]}} elsif st[3][1] = VAR then result &= {{"mov",eax, find_var(st[3][2])},{"mov",find_var(st[2]), eax}} elsif st[3][1] = CALLFUNC then result &= code_gen({st[3]}) result &= {{"mov",find_var(st[2]), eax}} else result &= expr_gen(st[3]) & {{"mov", find_var(st[2]), eax}} end if elsif st[1] = CALLPROC or st[1] = CALLFUNC then for a=length(st) to 3 by -1 do result &= Push(expr_gen(st[a])) end for locals = locals[length(st)-1..length(locals)] result &= {{"mov", eax, st[2]},{"call_near",eax}} if calling_convention = 0 and length(st) > 2 then result &= {{"add",esp,4*(length(st)-2)}} end if elsif st[1] = RETURN then if length(st) = 2 then result &= expr_gen(st[2]) end if result &= return_code elsif st[1] = POKE4 then if st[2][1] = CONST and st[3][1] = CONST then result &= {{"mov",{st[2][2]}, st[3][2]}} elsif st[2][1] = CONST and st[3][1] = VAR then result &= {{"mov",eax,find_var(st[3][2])},{"mov",{st[2][2]}, eax}} elsif st[2][1] = VAR and st[3][1] = CONST then result &= {{"mov",eax,find_var(st[2][2])},{"mov",{eax},st[3][2]}} elsif st[2][1] = VAR and st[3][1] = VAR then result &= {{"mov",edi,find_var(st[2][2])}, {"mov",eax,find_var(st[3][2])}, {"mov",{edi},eax}} else result &= Push(expr_gen(st[2])) result &= expr_gen(st[3]) result &= {{"pop", edi},{"mov",{edi},eax}} locals = locals[2..length(locals)] end if elsif st[1] = ADDTO then if st[3][1] = CONST then result &= {{"add",find_var(st[2]), st[3][2]}} elsif st[3][1] = VAR then result &= {{"mov",eax,find_var(st[3][2])},{"add",find_var(st[2]),eax}} else result &= expr_gen(st[3]) & {{"add",find_var(st[2]),eax}} end if elsif st[1] = WHILE then tmp = code_gen(st[3]) tmp = branch_gen(st) & jump_gen(st, 2+length(tmp)) & exit_gen(tmp) result &= tmp & {{"jmp",-length(tmp)}} elsif st[1] = EXIT then result &= {{"exit"}} elsif st[1] = IF then if length(st) = 3 then tmp = code_gen(st[3]) tmp = branch_gen(st) & jump_gen(st, 1+length(tmp)) & tmp result &= tmp elsif length(st) = 4 then tmp = code_gen(st[3]) tmp = branch_gen(st) & jump_gen(st, 2+length(tmp)) & tmp result &= tmp tmp = code_gen(st[4]) result &= {{"jmp",1+length(tmp)}} & tmp end if else puts(2, "error\n") end if end for return result end function global function subroutine(sequence arguments, sequence sub_locals, sequence code, integer main) sequence result locals = sub_locals & {"_return_address"} & arguments result = {} return_code = {} if main then result &= {{"pushad"}} return_code &= {{"popad"}} end if if length(sub_locals) then result &= {{"sub", esp, 4*length(sub_locals)}} return_code = {{"add",esp, 4*length(sub_locals)}} & return_code end if return_code &= {{"ret",4*length(arguments)*calling_convention}} result &= code_gen(code) if length(result) < length(return_code) or not equal(return_code, result[length(result)-length(return_code)+1..length(result)]) then result &= return_code end if return assemble(result) end function global function memorize(sequence code) atom c c = allocate(length(code)) poke(c, code) return c end function