pavone@185: { pavone@185: //commutative ops pavone@185: _add <- 0 pavone@185: _and <- 1 pavone@185: _or <- 2 pavone@185: _xor <- 3 pavone@185: //non-commutative ops pavone@185: _sub <- 4 pavone@185: _cmp <- 5 pavone@185: _not <- 6 pavone@185: _sl <- 7 pavone@185: _asr <- 8 pavone@185: _lsr <- 9 pavone@185: _rol <- 10 pavone@185: _ror <- 11 pavone@185: _mov <- 12 pavone@185: _call <- 13 pavone@185: _ret <- 14 pavone@185: _skipif <- 15 pavone@195: _save <- 16 pavone@185: pavone@185: _names <- #[ pavone@185: "add" pavone@185: "and" pavone@185: "or" pavone@185: "xor" pavone@185: "sub" pavone@185: "cmp" pavone@185: "not" pavone@185: "sl" pavone@185: "asr" pavone@185: "lsr" pavone@185: "rol" pavone@185: "ror" pavone@185: "mov" pavone@185: "call" pavone@185: "ret" pavone@185: "skipIf" pavone@195: "save" pavone@185: ] pavone@185: pavone@185: op3:a:b:out:size <- :_opcode :_ina :_inb :_out :_size { pavone@185: #{ pavone@185: opcode <- { _opcode } pavone@185: ina <- { _ina } pavone@185: inb <- { _inb } pavone@185: commutative? <- { _opcode < _sub } pavone@185: out <- { _out } pavone@185: size <- { _size } pavone@185: numops <- { 3 } pavone@185: name <- { _names get: _opcode } pavone@185: string <- { name . " " . (string: _ina) . " " . (string: _inb) . " " . (string: _out) . " " . (string: _size) } pavone@189: recordUsage:at <- :tracker :address { pavone@189: if: (not: (_ina isInteger?)) { pavone@189: _ina recordUsage: tracker at: 0 | address withSize: _size pavone@189: } pavone@189: _inb recordUsage: tracker at: 0 | address withSize: _size pavone@189: _out recordUsage: tracker at: 1 | address withSize: _size pavone@189: } pavone@200: assignRegs:at:withSource:andUsage <- :assignments :at :regSrc :usage { pavone@194: newa <- if: (not: (_ina isInteger?)) { pavone@194: _ina assign: assignments withSource: regSrc pavone@194: } else: { _ina } pavone@194: newb <- _inb assign: assignments withSource: regSrc pavone@194: newout <- _out assign: assignments withSource: regSrc pavone@194: op3: _opcode a: newa b: newb out: newout size: _size pavone@194: } pavone@185: } pavone@185: } pavone@185: op2:in:out:size <- :_opcode :_in :_out :_size { pavone@185: #{ pavone@185: opcode <- { _opcode } pavone@185: in <- { _in } pavone@185: out <- { _out } pavone@185: size <- { _size } pavone@185: numops <- { 2 } pavone@185: name <- { _names get: _opcode } pavone@185: string <- { name . " " . (string: _in) . " " . (string: _out) . " " . (string: _size) } pavone@189: recordUsage:at <- :tracker :address { pavone@189: if: (not: (_in isInteger?)) { pavone@189: _in recordUsage: tracker at: 0 | address withSize: _size pavone@189: } pavone@189: _out recordUsage: tracker at: 1 | address withSize: _size pavone@189: } pavone@200: assignRegs:at:withSource:andUsage <- :assignments :at :regSrc :usage { pavone@194: newin <- if: (not: (_in isInteger?)) { pavone@194: _in assign: assignments withSource: regSrc pavone@194: } else: { _in } pavone@194: newout <- _out assign: assignments withSource: regSrc pavone@194: op2: _opcode in: newin out: newout size: _size pavone@194: } pavone@185: } pavone@185: } pavone@185: op1:arg:size <- :_opcode :_arg :_size { pavone@185: #{ pavone@185: opcode <- { _opcode } pavone@185: arg <- { _arg } pavone@185: size <- { _size } pavone@185: numops <- { 1 } pavone@185: name <- { _names get: _opcode } pavone@185: string <- { name . " " . (string: _arg) . " " . (string: _size) } pavone@189: recordUsage:at <- :tracker :address { pavone@189: if: (not: (_arg isInteger?)) { pavone@189: _arg recordUsage: tracker at: address withSize: _size pavone@189: } pavone@189: } pavone@200: assignRegs:at:withSource:andUsage <- :assignments :at :regSrc :usage { pavone@194: newarg <- if: (not: (_arg isInteger?)) { pavone@194: _arg assign: assignments withSource: regSrc pavone@194: } else: { _arg } pavone@194: op1: _opcode arg: newarg size: _size pavone@194: } pavone@185: } pavone@185: } pavone@185: pavone@185: _sizenames <- #["b" "w" "l" "q"] pavone@193: _size <- :_bytes { pavone@185: #{ pavone@193: bytes <- { _bytes } pavone@193: string <- { pavone@193: idx <- if: _bytes = 8 { 3 } else: { _bytes / 2} pavone@193: _sizenames get: idx pavone@193: } pavone@185: = <- :other { pavone@193: _bytes = (other bytes) pavone@185: } pavone@185: <= <- :other { pavone@193: _bytes <= (other bytes) pavone@185: } pavone@185: >= <- :other { pavone@193: _bytes >= (other bytes) pavone@185: } pavone@185: > <- :other { pavone@193: _bytes > (other bytes) pavone@185: } pavone@185: < <- :other { pavone@193: _bytes < (other bytes) pavone@185: } pavone@185: } pavone@185: } pavone@193: byte <- _size: 1 pavone@193: word <- _size: 2 pavone@193: long <- _size: 4 pavone@193: quad <- _size: 8 pavone@185: pavone@185: _retr <- #{ pavone@185: isInteger? <- { false } pavone@185: register? <- { true } pavone@185: argument? <- { false } pavone@185: return? <- { true } pavone@185: string <- { "retr" } pavone@185: = <- :other { pavone@185: (not: (other isInteger?)) && (other register?) && (other return?) pavone@185: } pavone@189: != <- :other { pavone@189: not: self = other pavone@189: } pavone@189: recordUsage:at:withSize <- :tracker :address :size { pavone@189: //TODO: Figure out what tracking is necessary here pavone@189: } pavone@194: assign:withSource <- :assignments :regSrc { pavone@194: regSrc allocRet pavone@194: } pavone@185: } pavone@185: pavone@185: _condnames <- #[ pavone@185: "eq" pavone@185: "neq" pavone@185: "ge" pavone@185: "le" pavone@185: "gr" pavone@185: "ls" pavone@185: "uge" pavone@185: "ule" pavone@185: "ugr" pavone@185: "uls" pavone@185: ] pavone@185: condition <- :num { pavone@185: #{ pavone@185: cc <- { num } pavone@185: string <- { _condnames get: num } pavone@185: = <- :other { num = (other cc) } pavone@185: } pavone@185: } pavone@185: _eq <- condition: 0 pavone@185: _neq <- condition: 1 pavone@185: _ge <- condition: 2 pavone@185: _le <- condition: 3 pavone@185: _gr <- condition: 4 pavone@185: _ls <- condition: 5 pavone@185: _uge <- condition: 6 pavone@185: _ule <- condition: 7 pavone@185: _ugr <- condition: 8 pavone@185: _uls <- condition: 9 pavone@185: pavone@185: #{ pavone@185: b <- { byte } pavone@185: w <- { word } pavone@185: l <- { long } pavone@185: q <- { quad } pavone@185: pavone@185: eq <- { _eq } pavone@185: neq <- { _neq } pavone@185: pavone@185: //signed conditions pavone@185: ge <- { _ge } pavone@185: le <- { _le } pavone@185: gr <- { _gr } pavone@185: ls <- { _ls } pavone@185: pavone@185: //unsigned conditions pavone@185: uge <- { _uge } pavone@185: ule <- { _ule } pavone@185: ugr <- { _ugr } pavone@185: uls <- { _uls } pavone@185: pavone@185: pavone@185: reg <- :num { pavone@185: #{ pavone@185: isInteger? <- { false } pavone@185: register? <- { true } pavone@185: argument? <- { false } pavone@185: return? <- { false } pavone@185: regnum <- { num } pavone@185: string <- { "r" . (string: num) } pavone@185: = <- :other { pavone@185: (not: (other isInteger?)) && (other register?) && (not: (other argument?)) && (not: (other return?)) && num = (other regnum) pavone@185: } pavone@189: != <- :other { pavone@189: not: self = other pavone@189: } pavone@189: recordUsage:at:withSize <- :tracker :address :size { pavone@189: tracker reg: self usedAt: address withSize: size pavone@189: } pavone@194: assign:withSource <- :assignments :regSrc { pavone@194: assignments get: self pavone@194: } pavone@185: } pavone@185: } pavone@185: arg <- :num { pavone@185: #{ pavone@185: isInteger? <- { false } pavone@185: register? <- { true } pavone@185: argument? <- { true } pavone@185: return? <- { false } pavone@185: argnum <- { num } pavone@185: string <- { "a" . (string: num) } pavone@185: = <- :other { pavone@185: (not: (other isInteger?)) && (other register?) && (other argument?) && num = (other regnum) pavone@185: } pavone@189: != <- :other { pavone@189: not: self = other pavone@189: } pavone@189: recordUsage:at:withSize <- :tracker :address :size { pavone@189: tracker arg: self usedAt: address withSize: size pavone@189: } pavone@194: assign:withSource <- :assignments :regSrc { pavone@194: regSrc allocArg: num pavone@194: } pavone@185: } pavone@185: } pavone@185: retr <- { _retr } pavone@185: pavone@189: base:offset <- :_base :_offset { pavone@189: #{ pavone@189: base <- { _base } pavone@189: offset <- { _offset } pavone@189: string <- { pavone@189: start <- if: _offset = 0 { "" } else: { (string: _offset) } pavone@189: start . "[" . (string: _base) . "]" pavone@189: } pavone@189: recordUsage:at:withSize <- :tracker :address :size { pavone@189: _base recordUsage: tracker at: address withSize: size pavone@189: } pavone@189: } pavone@189: } pavone@189: pavone@185: add <- :ina inb out size { pavone@185: op3: _add a: ina b: inb out: out size: size pavone@185: } pavone@185: pavone@185: sub <- :ina inb out size { pavone@185: op3: _sub a: ina b: inb out: out size: size pavone@185: } pavone@185: pavone@185: cmp <- :ina inb out size { pavone@185: op3: _cmp a: ina b: inb out: out size: size pavone@185: } pavone@185: pavone@185: and <- :ina inb out size { pavone@185: op3: _and a: ina b: inb out: out size: size pavone@185: } pavone@185: pavone@185: or <- :ina inb out size { pavone@185: op3: _or a: ina b: inb out: out size: size pavone@185: } pavone@185: pavone@185: xor <- :ina inb out size { pavone@185: op3: _xor a: ina b: inb out: out size: size pavone@185: } pavone@185: pavone@185: bnot <- :in out size { pavone@185: op2: _not in: in out: out size: size pavone@185: } pavone@185: pavone@185: sl <- :shift in out size { pavone@185: op3: _sl a: shift b: in out: out size: size pavone@185: } pavone@185: pavone@185: asr <- :shift in out size { pavone@185: op3: _asr a: shift b: in out: out size: size pavone@185: } pavone@185: pavone@185: lsr <- :shift in out size { pavone@185: op3: _lsr a: shift b: in out: out size: size pavone@185: } pavone@185: pavone@185: rol <- :rot in out size { pavone@185: op3: _rol a: rot b: in out: out size: size pavone@185: } pavone@185: pavone@185: ror <- :rot in out size { pavone@185: op3: _ror a: rot b: in out: out size: size pavone@185: } pavone@185: pavone@185: mov <- :in out size { pavone@185: op2: _mov in: in out: out size: size pavone@185: } pavone@185: pavone@185: call:withArgs <- :_target :_args { pavone@185: #{ pavone@185: opcode <- { _call } pavone@185: target <- { _target } pavone@185: args <- { _args } pavone@185: numops <- { 0 } pavone@185: name <- { _names get: _call } pavone@185: string <- { pavone@185: argstr <- _args map: :el { pavone@185: string: el pavone@185: } pavone@185: name . " " . (string: _target) . " " . (argstr join: " ") pavone@185: } pavone@189: recordUsage:at <- :tracker :address { pavone@189: if: (not: (_target isString?)) { pavone@189: //TODO: use size l for 32-bit targets or an abstract pointer size pavone@189: _target recordUsage: tracker at: address withSize: q pavone@189: } pavone@189: foreach: _args :_ arg { pavone@189: //TODO: have some mechanism for properly expressing sizes of arguments pavone@189: arg recordUsage: tracker at: address withSize: q pavone@189: } pavone@189: } pavone@200: assignRegs:at:withSource:andUsage <- :assignments :address :regSrc :usage { pavone@194: newtarget <- if: (_target isString?) { _target } else: { pavone@194: _target assign: assignments withSource: regSrc pavone@194: } pavone@194: newargs <- _args map: :arg { pavone@194: if: (arg isInteger?) { arg } else: { pavone@194: arg assign: assignments withSource: regSrc pavone@194: } pavone@194: } pavone@200: newcall <- call: newtarget withArgs: newargs pavone@200: regSrc returnAll pavone@200: raddress <- address reverse pavone@200: foreach: (usage liveArgsAt: raddress) :_ arg { pavone@200: regSrc allocArg: (arg num) pavone@200: } pavone@200: foreach: (usage liveRegsAt: raddress) :_ reg { pavone@200: regSrc allocSpecific: (assignments get: reg) pavone@200: } pavone@200: tosave <- regSrc needSaveForCall pavone@200: if: (tosave length) > 0 { pavone@200: save: tosave #[newcall] pavone@200: } else: { pavone@200: newcall pavone@200: } pavone@194: } pavone@185: } pavone@185: } pavone@185: pavone@185: return <- :val size { pavone@185: op1: _ret arg: val size: size pavone@185: } pavone@185: skipIf <- :_cond _toskip { pavone@185: #{ pavone@185: opcode <- { _skipif } pavone@185: toskip <- { _toskip } pavone@185: cond <- { _cond } pavone@185: numops <- { 0 } pavone@185: name <- { _names get: _skipif } pavone@185: string <- { pavone@185: block <- (_toskip map: :el { string: el }) join: "\n\t" pavone@185: if: (_toskip length) > 0 { pavone@185: block <- "\n\t" . block . "\n" pavone@185: } pavone@185: name . " " . (string: _cond) . " {" . block . "}" pavone@185: } pavone@189: recordUsage:at <- :tracker :address { pavone@189: foreach: _toskip :idx inst { pavone@189: inst recordUsage: tracker at: idx | address pavone@189: } pavone@189: } pavone@200: assignRegs:at:withSource:andUsage <- :assignments :address :regSrc :usage { pavone@200: newskip <- #[] pavone@200: foreach: _toskip :idx inst { pavone@200: newskip append: (inst assignRegs: assignments at: idx | address withSource: regSrc andUsage: usage) pavone@194: } pavone@194: skipIf: _cond newskip pavone@194: } pavone@200: to2OpInst <- { pavone@200: skipIf: _cond (to2Op: _toskip) pavone@200: } pavone@185: } pavone@185: } pavone@203: save <- :regs :_scope{ pavone@195: #{ pavone@195: opcode <- { _save } pavone@195: numops <- { 0 } pavone@195: name <- { _names get: _save } pavone@203: tosave <- { regs } pavone@203: scope <- { _scope } pavone@195: string <- { pavone@203: block <- _scope join: "\n\t" pavone@203: if: (_scope length) > 0 { pavone@195: block <- "\n\t" . block . "\n" pavone@195: } pavone@195: name . " " . (regs join: " ") . " {" . block . "}" pavone@195: } pavone@200: to2OpInst <- { pavone@203: save: regs (to2Op: _scope) pavone@200: } pavone@195: } pavone@195: } pavone@185: pavone@189: allocRegs:withSource <- :instarr:regSrc { pavone@189: _regMap <- dict linear pavone@189: _argMap <- dict linear pavone@189: pavone@189: _usageTracker <- :_firstUsage { pavone@189: #{ pavone@189: firstUsage <- _firstUsage pavone@189: lastUsage <- _firstUsage pavone@189: useCount <- 0 pavone@189: maxSize <- byte pavone@189: usedAt:withSize <- :address :size { pavone@189: useCount <- useCount + 1 pavone@189: lastUsage <- address pavone@189: if: size > maxSize { pavone@189: maxSize <- size pavone@189: } pavone@189: } pavone@189: string <- { pavone@189: "Uses: " . useCount . ", FirstUse: " . (firstUsage join: ":") . ", Last Use: " . (lastUsage join: ":") . ", Max Size: " . maxSize pavone@189: } pavone@189: } pavone@189: } pavone@189: pavone@189: _maxUses <- 0 pavone@200: liveFrom:to <- :regs :from :to { pavone@200: live <- #[] pavone@200: foreach: regs :reg usage { pavone@200: if: ((usage lastUsage) addrGreatEq: from) && ((usage firstUsage) addrLessEq: to) { pavone@200: live append: reg pavone@200: } pavone@200: } pavone@200: live pavone@200: } pavone@189: regUsage <- #{ pavone@189: reg:usedAt:withSize <- :reg :address :size { pavone@193: raddress <- address reverse pavone@189: usage <- _regMap get: reg elseSet: { pavone@193: _usageTracker: raddress pavone@189: } pavone@193: usage usedAt: raddress withSize: size pavone@189: if: (usage useCount) > _maxUses { pavone@189: _maxUses <- usage useCount pavone@189: } pavone@189: } pavone@189: arg:usedAt:withSize <- :arg :address :size { pavone@193: raddress <- address reverse pavone@189: usage <- _argMap get: arg elseSet: { pavone@189: _usageTracker: [0 0] pavone@189: } pavone@193: usage usedAt: raddress withSize: size pavone@189: } pavone@200: pavone@200: liveRegsAt <- :address { pavone@200: _regMap liveFrom: address to: address pavone@200: } pavone@200: liveArgsAt <- :address { pavone@200: _argMap liveFrom: address to: address pavone@200: } pavone@200: pavone@189: print <- { pavone@189: foreach: _regMap :reg usage { pavone@189: print: (string: reg) . " | " . (string: usage) . "\n" pavone@189: } pavone@189: foreach: _argMap :arg usage { pavone@189: print: (string: arg) . " | " . (string: usage) . "\n" pavone@189: } pavone@189: } pavone@189: } pavone@189: foreach: instarr :idx inst { pavone@189: inst recordUsage: regUsage at: [idx] pavone@189: } pavone@189: print: regUsage pavone@193: pavone@193: addrLessEq <- :left :right { pavone@193: lesseq <- true pavone@193: while: { lesseq && (not: (left empty?)) && (not: (right empty?)) } do: { pavone@193: if: (left value) > (right value) { pavone@193: lesseq <- false pavone@193: } else: { pavone@193: if: (left value) < (right value) { pavone@193: left <- [] pavone@193: } else: { pavone@193: left <- left tail pavone@193: right <- right tail pavone@193: } pavone@193: } pavone@193: } pavone@193: lesseq pavone@193: } pavone@193: pavone@193: addrGreatEq <- :left :right { pavone@193: greateq <- true pavone@193: while: { greateq && (not: (left empty?)) && (not: (right empty?)) } do: { pavone@193: if: (left value) < (right value) { pavone@193: greateq <- false pavone@193: } else: { pavone@193: if: (left value) > (right value) { pavone@193: left <- [] pavone@193: } else: { pavone@193: left <- left tail pavone@193: right <- right tail pavone@193: } pavone@193: } pavone@193: } pavone@193: greateq pavone@193: } pavone@193: pavone@193: _assignments <- dict linear pavone@193: curuses <- _maxUses pavone@193: while: { curuses > 0 && (_assignments length) < (_regMap length) } do: { pavone@193: foreach: _regMap :reg usage { pavone@193: if: (usage useCount) = curuses { pavone@193: liveArgs <- _argMap liveFrom: (usage firstUsage) to: (usage lastUsage) pavone@193: foreach: liveArgs :_ arg { pavone@193: regSrc allocArg: (arg num) pavone@193: } pavone@193: pavone@193: liveRegs <- _regMap liveFrom: (usage firstUsage) to: (usage lastUsage) pavone@193: print: (string: reg) . " | Live: " . (liveRegs join: ", ") . ", Live Args: " . (liveArgs join: ", ") . "\n" pavone@193: foreach: liveRegs :_ reg { pavone@193: if: (_assignments contains?: reg) { pavone@193: regSrc allocSpecific: (_assignments get: reg) pavone@193: } pavone@193: } pavone@193: _assignments set: reg (regSrc alloc: (usage maxSize)) pavone@193: pavone@193: regSrc returnAll pavone@193: } pavone@193: } pavone@193: curuses <- curuses - 1 pavone@193: } pavone@193: print: "\n\nAssignments:\n\n" pavone@193: foreach: _assignments :reg assign { pavone@193: print: (string: reg) . " = " . assign . "\n" pavone@193: } pavone@194: pavone@200: withassign <- #[] pavone@200: foreach: instarr :idx inst { pavone@200: withassign append: (inst assignRegs: _assignments at: [idx] withSource: regSrc andUsage: regUsage) pavone@194: } pavone@195: psave <- regSrc needSaveProlog pavone@195: if: (psave length) > 0 { pavone@195: withassign <- #[save: psave withassign] pavone@195: } pavone@195: withassign pavone@189: } pavone@189: pavone@185: //used to convert IL to a format suitable for a 2-operand architecture pavone@185: //should be run after register allocation (I think....) pavone@185: to2Op <- :instarr { pavone@185: instarr fold: #[] with: :newarr inst { pavone@185: if: (inst numops) = 3 { pavone@185: if: (inst inb) = (inst out) { pavone@185: newarr append: (op2: (inst opcode) in: (inst ina) out: (inst out) size: (inst size)) pavone@185: } else: { pavone@185: if: (inst commutative?) && (inst ina) = (inst out) { pavone@185: newarr append: (op2: (inst opcode) in: (inst inb) out: (inst out) size: (inst size)) pavone@185: } else: { pavone@185: newarr append: (mov: (inst inb) (inst out) (inst size)) pavone@185: newarr append: (op2: (inst opcode) in: (inst ina) out: (inst out) size: (inst size)) pavone@185: } pavone@185: } pavone@185: } else: { pavone@185: if: (inst numops) = 2 && (inst opcode) != _mov { pavone@185: if: (inst in) != (inst out) { pavone@185: newarr append: (mov: (inst in) (inst out) (inst size)) pavone@185: } pavone@185: newarr append: (op1: (inst opcode) val: (inst out) size: (inst size)) pavone@185: } else: { pavone@200: if: (inst opcode) = _skipif || (inst opcode) = _save { pavone@200: newarr append: (inst to2OpInst) pavone@200: } else: { pavone@200: newarr append: inst pavone@200: } pavone@185: } pavone@185: } pavone@185: } pavone@185: } pavone@185: pavone@203: toBackend <- :program :backend { pavone@203: prepped <- program map: :fun { pavone@203: backend adjustIL: fun pavone@203: } pavone@203: labels <- prepped map: :_ { pavone@203: backend label pavone@203: } pavone@203: outprog <- #[] pavone@203: foreach: prepped :name instarr { pavone@203: outprog append: (labels get: name) pavone@203: foreach: instarr :_ inst { pavone@203: backend convertIL: inst to: outprog withLabels: labels pavone@203: } pavone@203: } pavone@203: outprog pavone@203: } pavone@203: pavone@185: main <- { pavone@203: prog <- dict linear pavone@203: pavone@185: fib <- #[ pavone@185: sub: 2 (arg: 0) (reg: 0) q pavone@185: skipIf: ge #[ pavone@185: return: 1 q pavone@185: ] pavone@185: call: "fib" withArgs: #[reg: 0] pavone@185: mov: retr (reg: 1) q pavone@185: add: 1 (reg: 0) (reg: 2) q pavone@185: call: "fib" withArgs: #[reg: 2] pavone@185: add: retr (reg: 1) (reg: 3) q pavone@185: return: (reg: 3) q pavone@185: ] pavone@185: print: "Original:\n\n" pavone@185: foreach: fib :idx inst { pavone@185: print: (string: inst) . "\n" pavone@185: } pavone@203: prog set: "fib" fib pavone@203: pavone@203: mprog <- prog toBackend: x86 pavone@203: ba <- bytearray executableFromBytes: mprog pavone@203: res <- ba runWithArg: 30u64 pavone@203: print: (string: res) . "\n" pavone@203: 0 pavone@185: } pavone@185: } pavone@185: }