15 function regalloc_reset()
 
  17         for k,v in pairs(regstate) do
 
  22 function regalloc_tmp()
 
  23         for k,v in pairs(regstate) do           -- first look for an empty one
 
  24                 if not v.contains then
 
  25                         regstate[k] = { contains = " tmp", name = k, locked = 0 }
 
  26                         table.insert(regvars, regstate[k])
 
  30         for k,v in pairs(regstate) do           -- now look for an unlocked variable to evict
 
  31                 if regalloc_unlocked(v) and v.contains ~= " tmp" then
 
  33                         regstate[k] = { contains = " tmp", name = k, locked = 0 }
 
  34                         table.insert(regvars, regstate[k])
 
  38         for k,v in pairs(regstate) do           -- now look for an unlocked temporary to evict
 
  39                 if regalloc_unlocked(v) then
 
  41                         regstate[k] = { contains = " tmp", name = k, locked = 0 }
 
  42                         table.insert(regvars, regstate[k])
 
  46         error("register allocator: tried to allocate a temporary, but couldn't find anything to evict")
 
  49 function regalloc_get(var)
 
  50         for k,v in pairs(regvars) do
 
  51                 if v.contains == var then
 
  55         local reg = { contains = var, locked = 0, evicted = true, stackpos = var.stackpos }
 
  56         table.insert(regvars, reg)
 
  60 function regalloc_lock(reg)
 
  64         reg.locked = reg.locked + 1
 
  67 function regalloc_unlock(reg)
 
  68         assert(reg.locked > 0)
 
  69         reg.locked = reg.locked - 1
 
  72 function regalloc_unlocked(reg)
 
  73         return not reg.locked or reg.locked == 0
 
  76 function regalloc_free(reg)
 
  77         if reg.contains == " tmp" then
 
  78                 for k,v in pairs(regvars) do
 
  80                                 table.remove(regvars, k)
 
  83                 regstate[reg.name] = {}
 
  91 function regalloc_savestate(save)
 
  92         -- store all NON-evicted variables that MIGHT get evicted in the savestate
 
  93         for k,v in pairs(regstate) do
 
  94                 if v.contains and regalloc_unlocked(v) then
 
  95                         save[v] = { var = v, reg = v.name, dirty = v.dirty }
 
 100 function regalloc_restorestate(save)
 
 101         -- restore any variables that got evicted
 
 102         for k,v in pairs(regstate) do
 
 103                 if v.dirty and ((not save[v]) or (not save[v].dirty)) then
 
 107         for k,v in pairs(save) do
 
 108                 regalloc_unevict(v.var, v.reg)
 
 112 function regalloc_printstate()
 
 113         for k,reg in pairs(regstate) do
 
 114                 if not reg.contains then
 
 116                         for k1,v1 in pairs(reg) do
 
 117                                 print(k.."."..tostring(k1)..": "..tostring(v1))
 
 121                         if reg.contains == " tmp" then
 
 122                                 name = "compiler internal variable"
 
 123                         elseif reg.contains.name then
 
 124                                 name = reg.contains.name
 
 126                                 name = "unknown variable"
 
 128                         print(k..": "..name.." ("..
 
 129                                 (reg.evicted and "evicted" or "not evicted") .. ", "..
 
 130                                 ((not regalloc_unlocked(reg)) and "locked" or "not locked") .. ", " ..
 
 131                                 (reg.dirty and "dirty" or "clean") .. ")")
 
 136 function regalloc_evict(reg)
 
 137         assert(reg.contains, "can't evict nil")
 
 138         assert(reg.contains ~= " tmp", "tmp eviction not implemented")
 
 145                 regstate[reg.name] = {}
 
 150 function regalloc_clean(reg)
 
 151         assert(reg.contains, "can't clean nil")
 
 152         assert(not reg.evicted, "register already evicted?")
 
 153         assert(regalloc_unlocked(reg), "must be unlocked to clean")
 
 155         local name = "unknown variable"
 
 156         if type(reg.contains) == "string" then
 
 158         elseif type(reg.contains) == "table" and reg.contains.name then
 
 159                 name = reg.contains.name
 
 161         name = "`"..name.."'"
 
 165         -- first off, look for an easy eviction -- is there an empty register?
 
 166         for k,v in pairs(regstate) do
 
 167                 if not v.contains then
 
 168                         local tmp = regalloc_tmp()
 
 170                         emit_opcode("mov", tmp.name..", #"..(reg.stackpos-maxstack-1), "easy evict "..name.." to stack")
 
 171                         emit_opcode("add", tmp.name..", sp")
 
 172                         emit_opcode("sto", "["..tmp.name.."], "..reg.name)
 
 177         -- damnit, we have to do an in-place evict
 
 180         emit_opcode("push", "sp, "..reg.name, "in-place evict "..name)
 
 181         -- Step 2 -- Move offset from new sp to desired place into rN
 
 182         emit_opcode("mov", reg.name..", #"..(reg.stackpos-maxstack-2))
 
 183         -- Step 3 -- Add sp to result to get an absolute pointer
 
 184         emit_opcode("add", reg.name..", sp")
 
 185         -- Step 4 -- Load sp with original contents of rN
 
 186         emit_opcode("pop", "sp, sp")
 
 187         -- Step 5 -- Store original contents in address now stored in rN
 
 188         emit_opcode("sto", "["..reg.name.."], sp")
 
 189         -- Step 6 -- Move offset to get back to correct sp into sp
 
 190         emit_opcode("mov", "sp, #"..(maxstack-reg.stackpos +1))
 
 191         -- Step 7 -- Add rN to sp; sp is now restored
 
 192         emit_opcode("add", "sp, "..reg.name)
 
 193         -- In this case, the variable is toast. Sorry.
 
 194         regstate[reg.name] = {}
 
 199 function regalloc_unevict(reg, wantreg)
 
 200         assert(reg.contains, "can't unevict a nil pointer")
 
 201         local name = "unknown variable"
 
 202         if type(reg.contains) == "string" then
 
 204         elseif type(reg.contains) == "table" and reg.contains.name then
 
 205                 name = reg.contains.name
 
 207         name = "`"..name.."'"
 
 209         if not reg.evicted and wantreg and wantreg ~= reg.name then
 
 210                 assert(regalloc_unlocked(regstate[wantreg]), "tried to unevict with a locked wantreg")
 
 211                 if regstate[wantreg].contains then
 
 212                         emit_opcode("push", "sp, "..wantreg, "unevict: swap "..name.." and "..wantreg)
 
 213                         emit_opcode("mov", wantreg..", "..reg.name)
 
 214                         emit_opcode("pop", "sp, "..reg.name)
 
 215                         regstate[reg.name] = regstate[wantreg]
 
 216                         regstate[reg.name].name = reg.name
 
 217                         regstate[wantreg] = reg
 
 218                         regstate[wantreg].name = wantreg
 
 221                         emit_opcode("mov", wantreg..", "..reg.name, "unevict: move "..name.." to "..wantreg)
 
 222                         regstate[reg.name] = {}
 
 223                         regstate[wantreg] = reg
 
 224                         regstate[wantreg].name = wantreg
 
 225                         return          -- double wee!
 
 227         elseif not reg.evicted then
 
 228                 return  -- well, if we don't actually have to do anything, ...!
 
 230                 assert(reg.contains ~= " tmp", "tmp uneviction not implemented")
 
 233                 for k,v in pairs(regstate) do   -- look for an empty
 
 234                         if not v.contains and not wantreg then
 
 238                 for k,v in pairs(regstate) do   -- look for a nontmp that's not dirty
 
 239                         if regalloc_unlocked(v) and v.contains ~= " tmp" and not v.dirty and not wantreg then
 
 244                 for k,v in pairs(regstate) do   -- look for any nontmp
 
 245                         if regalloc_unlocked(v) and v.contains ~= " tmp" and not wantreg then
 
 250                 for k,v in pairs(regstate) do   -- look for anything to evict
 
 251                         if regalloc_unlocked(v) and not wantreg then
 
 256                 assert(wantreg and regalloc_unlocked(regstate[wantreg]), "could not find an unlocked register to evict to")
 
 257                 emit_opcode("mov", wantreg..", #"..(reg.stackpos-maxstack-1), "unevict: pull "..name.." from stack to "..wantreg)
 
 258                 emit_opcode("add", wantreg..", sp")
 
 259                 emit_opcode("ldr", wantreg..", ["..wantreg.."]")
 
 262                 regstate[wantreg] = reg
 
 265         error("something got fucked up")
 
 268 function regalloc_unevict_all_tmp()
 
 269         for k,v in pairs(regvars) do
 
 270                 if v.evicted and v.contains == " tmp" then
 
 276 function regalloc_destroy(variable)
 
 277         for k,v in pairs(regvars) do
 
 278                 if v.contains == variable and not v.evicted then
 
 279                         regstate[v.name] = {}
 
 285 local cursection = nil
 
 287         outf:write(text.."\n")
 
 290 function emit_section(section)
 
 291         if cursection == section then
 
 294         emit("\t.section "..section)
 
 298 function emit_label(label)
 
 302 function emit_opcode(opcode, params, comment)
 
 304                 emit("\t"..opcode.."\t"..params.."\t\t\t@ "..comment)
 
 306                 emit("\t"..opcode.."\t"..params)
 
 310 local globalvariables = {}
 
 311 function do_globalvariable(var)
 
 312         globalvariables[var.name] = { type = var.vtype }
 
 313         if var.vtype == "int" then
 
 314                 local initializer = var.initializer or 0
 
 317                 emit_opcode(".word",tostring(initializer))
 
 319                 error("unknown variable emit in do_globalvariable")
 
 323 function do_function(func)
 
 327         -- First, read in the arguments and register them as being on the stack.
 
 328         for k,v in pairs(func.args) do
 
 329                 stack[-k] = { name = v.name, type = v.type, stackpos = -k }
 
 330                 assert(v.type == "int", "can only handle ints on the stack right now")
 
 333         stack[0] = {name = " return address", type = "int" }    -- XXX not actually an int
 
 336         emit_section(".text")
 
 338         emit_opcode(".global", func.name)
 
 339         emit_label(func.name)
 
 340         local emitter = function(expr, emitter)
 
 341                 if expr.type == "stmtlist" then
 
 342                         local origstack = maxstack
 
 343                         local initialized = false
 
 345                         regalloc_savestate(savestate)
 
 346                         for k,v in pairs(expr.body) do
 
 347                                 if v.type == "variable" then
 
 348                                         assert(not initialized, "cannot have more variables after stack has been emitted")
 
 349                                         maxstack = maxstack + 1
 
 350                                         stack[maxstack] = { name = v.name, type = v.vtype, initializer = v.initializer, stackpos = maxstack }
 
 352                                         if not initialized and origstack ~= maxstack then
 
 353                                                 local reg = regalloc_tmp()
 
 355                                                 emit_opcode("mov", reg.name..", #"..tostring(maxstack-origstack), "increase stack size for variables")
 
 356                                                 emit_opcode("add", "sp, "..reg.name)
 
 358                                                 for k2,v2 in pairs(stack) do
 
 359                                                         if v2.initializer then
 
 360                                                                 local data = regalloc_tmp()     -- one for the data
 
 361                                                                 local addr = regalloc_tmp()     -- one for the stack address
 
 363                                                                 emit_opcode("mov", data.name..", #"..v2.initializer, "initialize variable "..v2.name)
 
 365                                                                 emit_opcode("mov", addr.name..", #"..tostring(k2-maxstack-1))
 
 366                                                                 emit_opcode("add", addr.name..", sp")
 
 367                                                                 emit_opcode("sto", "["..addr.name.."], "..data.name)
 
 375                                         local reg = emitter(v, emitter)
 
 381                         if origstack ~= maxstack then
 
 382                                 local reg = regalloc_tmp()
 
 383                                 emit_opcode("mov", reg.name..", #"..tostring(origstack-maxstack), "decrease stack size for variables")
 
 384                                 emit_opcode("add", "sp, "..reg.name)
 
 387                                 for i=origstack+1,maxstack do
 
 388                                         regalloc_destroy(stack[i])
 
 392                         regalloc_restorestate(savestate)
 
 393                 elseif expr.type == "expression" then
 
 394                         return emitter(expr.value, emitter)
 
 395                 elseif expr.type == "funccall" then
 
 396                         -- push args on the stack from last to first
 
 397                         for arg=table.getn(expr.args),1,-1 do
 
 398                                 local reg = emitter(expr.args[arg], emitter)
 
 399                                 assert(reg, "argument "..arg.." did not return a register")
 
 401                                 regalloc_unevict_all_tmp()
 
 402                                 emit_opcode("push","sp, "..reg.name, "push argument "..arg.." onto the stack for function call "..expr.name)
 
 404                                 maxstack = maxstack + 1
 
 406                         -- meh, I hate fucking around with the allocator's internal state here, XXX
 
 407                         for k,v in pairs(regstate) do
 
 408                                 assert(not(v.contains == " tmp" and not v.evicted), "it's too damn late now to evict tmp")
 
 409                                 if not v.evicted and v.contains then
 
 413                         local reg = regalloc_tmp()
 
 415                         emit_opcode("mov",reg.name..", #"..expr.name, "function call "..expr.name)
 
 416                         emit_opcode("call","sp, "..reg.name)
 
 417                         -- first thing: save the return value!
 
 418                         local retv = regalloc_tmp()
 
 420                         if reg.name == "r0" then        -- try hard to get r0 for the return value
 
 425                         if retv.name ~= "r0" then       -- wee o_O
 
 426                                 emit_opcode("mov", retv.name..", r0", "grab return value")
 
 428                         -- decrement the stack by the number of args
 
 429                         emit_opcode("mov",reg.name..", #-"..table.getn(expr.args), "clean up stack from function call")
 
 430                         emit_opcode("add","sp, "..reg.name)
 
 431                         maxstack = maxstack - table.getn(expr.args)
 
 434                 elseif expr.type == "while" then
 
 435                         local headlabel, taillabel = labelnum, labelnum + 1
 
 437                         labelnum = labelnum + 2
 
 438                         emit_label(".L"..headlabel)
 
 440                         local resreg = emitter(expr.expression, emitter)
 
 441                         assert(resreg, "while expression did not return a register")
 
 442                         regalloc_lock(resreg)
 
 443                         local tmp = regalloc_tmp()
 
 444                         emit_opcode("mov",tmp.name..", #0")
 
 445                         emit_opcode("tst",tmp.name..", "..resreg.name)
 
 446                         emit_opcode("moveq","pc, #.L"..taillabel, "conditional break from while")
 
 447                         regalloc_free(resreg)
 
 449                         regalloc_savestate(savestate)
 
 450                         local tmp = emitter(expr.chunk, emitter)
 
 454                         regalloc_restorestate(savestate)
 
 455                         emit_opcode("mov","pc, #.L"..headlabel)
 
 456                         emit_label(".L"..taillabel)
 
 457                 elseif expr.type == "return" then
 
 458                         local reg = emitter(expr.expression, emitter)
 
 459                         assert(reg, "return expression did not return a register")
 
 460                         regalloc_unevict(reg, "r0")
 
 462                         assert(reg.name == "r0", "failed to unevict to r0?")
 
 463                         local tmp = regalloc_tmp()
 
 465                         emit_opcode("mov", tmp.name..", #-"..maxstack, "clean up stack before return")
 
 466                         emit_opcode("add","sp, "..tmp.name)
 
 467                         emit_opcode("pop","sp, pc", "return")
 
 470                 elseif expr.type == "if" then
 
 471                         local taillabel = labelnum
 
 473                         labelnum = labelnum + 1
 
 474                         local resreg = emitter(expr.expression, emitter)
 
 475                         assert(resreg, "if expression did not return a register")
 
 476                         regalloc_lock(resreg)
 
 477                         local tmp = regalloc_tmp()
 
 478                         emit_opcode("mov",tmp.name..", #0")
 
 479                         emit_opcode("tst",tmp.name..", "..resreg.name)
 
 480                         emit_opcode("moveq","pc, #.L"..taillabel, "conditional exit from if")
 
 481                         regalloc_free(resreg)
 
 483                         regalloc_savestate(savestate)
 
 484                         local tmp = emitter(expr.chunk, emitter)
 
 488                         regalloc_restorestate(savestate)
 
 489                         emit_label(".L"..taillabel)
 
 490                 elseif expr.type == "operator" and expr.value ~= "=" then
 
 492                         local lhs = emitter(expr.left, emitter)
 
 493                         local rhs = emitter(expr.right, emitter)
 
 495                         assert(lhs, "lhs did not return a register")
 
 496                         assert(rhs, "rhs did not return a register")
 
 499                         if lhs.contains == " tmp" then
 
 501                         elseif rhs.contains == " tmp" then
 
 505                         if expr.value == "==" or expr.value == "<" or expr.value == ">" then
 
 506                                 local predicates = { ["=="] = "eq", ["<"] = "lt", [">"] = "gt" }
 
 511                                 -- tst is fucking backwards
 
 512                                 emit_opcode("tst",rhs.name..", "..lhs.name, "comparison operator "..expr.value)
 
 513                                 emit_opcode("mov",tmp.name..", #0")
 
 514                                 emit_opcode("mov"..predicates[expr.value], tmp.name..", #1")
 
 515                         elseif expr.value == "+" then
 
 521                                         emit_opcode("add",tmp.name..", "..rhs.name)
 
 522                                 elseif tmp == rhs then
 
 523                                         emit_opcode("add",tmp.name..", "..lhs.name)
 
 525                                         emit_opcode("mov",tmp.name..", "..lhs.name)
 
 526                                         emit_opcode("add",tmp.name..", "..rhs.name)
 
 528                         elseif expr.value == "-" then
 
 533                                 emit_opcode("not", rhs.name)
 
 534                                 local tmp2 = regalloc_tmp()
 
 536                                 emit_opcode("mov",tmp2.name..", #1")
 
 537                                 emit_opcode("add",rhs.name..", "..tmp2.name)
 
 540                                         emit_opcode("add",tmp.name..", "..rhs.name)
 
 541                                 elseif tmp == rhs then
 
 542                                         emit_opcode("add",tmp.name..", "..lhs.name)
 
 544                                         emit_opcode("mov",tmp.name..", "..lhs.name)
 
 545                                         emit_opcode("add",tmp.name..", "..rhs.name)
 
 548                                 print("Dunno how to handle operator "..expr.value)
 
 560                 elseif expr.type == "operator" and expr.value == "=" then
 
 561                         assert(expr.left.type == "identifier", "lhs has to be an identifier")
 
 562                         local lhs = emitter(expr.left, emitter)
 
 563                         local rhs = emitter(expr.right, emitter)
 
 564                         assert(rhs, "rhs did not return a register")
 
 568                         emit_opcode("mov",lhs.name..", "..rhs.name)
 
 571                 elseif expr.type == "identifier" then
 
 572                         -- find the variable on the stack
 
 574                         for k,v in pairs(stack) do
 
 575                                 if v.name == expr.value and not var then
 
 579                         for k,v in pairs(globalvariables) do
 
 580                                 if v.name == expr.value and not var then
 
 584                         assert(var, "I have no idea what identifier "..expr.value.." is")
 
 585                         return regalloc_get(var)
 
 586                 elseif expr.type == "number" then
 
 587                         local reg = regalloc_tmp()
 
 589                         emit_opcode("mov", reg.name..", #"..expr.value)
 
 593                         error("unknown expression type "..expr.type)
 
 596         emitter(func.body, emitter)     -- call into it, and light it off
 
 597         emit_opcode("pop", "sp, pc")
 
 600 function backend(parsetree, outfn)
 
 602                 outf = io.open(outfn, "w")
 
 606         emit("@ blargCPU output generated by jwcc")
 
 607         for k,v in pairs(parsetree) do
 
 608                 if v.type == "variable" then
 
 610                 elseif v.type == "function" then
 
 613                         error("invalid type in global deparse")