Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic-blocks-based intermediate language and flow analysis. #607

Merged
merged 21 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e1102f7
Making basic blocks i.r. from the tree i.r.
gabrielsferre Jun 20, 2024
97179c3
Adding option for printing basic blocks i.r.
gabrielsferre Jun 20, 2024
5c80ed6
Refactoring uninitialized.lua to use basic blocks
gabrielsferre Jun 20, 2024
66661ca
Generating C code using the basic blocks i.r.
gabrielsferre Jun 27, 2024
8c741d1
Not using ir.Cmd.Return on the basic blocks i.r. anymore
gabrielsferre Jun 27, 2024
730930d
Using kill sets at uninitialize.lua flow analysis
gabrielsferre Jun 27, 2024
58a70c3
Using depth-search topological order on block traversal
gabrielsferre Jun 27, 2024
9de27da
Changing gc.lua traversal of cmds so it uses blocks.
gabrielsferre Jun 25, 2024
666cf5a
Now the option --print-ir prints the basic blocks i.r.
gabrielsferre Jun 27, 2024
d6eb77f
Generating basic blocks i.r. from a.s.t.
gabrielsferre Jun 27, 2024
67050a6
Removing unused code from ir.lua
gabrielsferre Jun 27, 2024
dd66808
adding "repeat until" non-breaking loop test
gabrielsferre Jun 27, 2024
32f11e8
Fixing garbage collector's command traversal order
gabrielsferre Jun 28, 2024
21c7c71
Adding tests that currently break uninitialized.lua
gabrielsferre Jul 1, 2024
d7932dc
Replacing JmpIfFalse condition with ir.Cmd.CondSrc command
gabrielsferre Jul 1, 2024
71a1c49
Creating jump instructions and removing BasicBlocks jump properties
gabrielsferre Jul 1, 2024
dceb559
Taking away the "return" from i.r. printing
gabrielsferre Jul 3, 2024
17e8540
Making fixes for PR
gabrielsferre Jul 4, 2024
b61fc37
Having only one jump per basic block
gabrielsferre Jul 5, 2024
bd591f7
Replacing JmpIfFalse with JmpIf command
gabrielsferre Jul 6, 2024
57c1837
Prettifying generated C code
gabrielsferre Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions spec/execution_tests.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2318,6 +2318,14 @@ function execution_tests.run(compile_file, backend, _ENV, only_compile)
end
end

function m.non_breaking_loop2(): integer
local i = 1
repeat
if i == 42 then return i end
i = i + 1
until false
end

function m.initialize_inside_loop(): integer
local x: integer
repeat
Expand Down
185 changes: 84 additions & 101 deletions src/pallene/coder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ function Coder:c_value(value)
end
end

function Coder:c_label(index)
return "L" .. index
gabrielsferre marked this conversation as resolved.
Show resolved Hide resolved
end

-- The information for creating a C local var for a given Pallene local var.
function Coder:prepare_local_var(func, v_id)
local c_name = self:c_var(v_id)
Expand Down Expand Up @@ -458,7 +462,7 @@ function Coder:pallene_entry_point_definition(f_id)
local slots_needed = max_frame_size + self.max_lua_call_stack_usage[func]

local setline = ""
local void_frameexit = ""
local frameexit = ""
if self.flags.use_traceback then
table.insert(prologue, util.render([[
/**/
Expand All @@ -469,10 +473,7 @@ function Coder:pallene_entry_point_definition(f_id)
}));

setline = string.format("PALLENE_SETLINE(%d);", func.loc and func.loc.line or 0)

if #func.typ.ret_types == 0 then
void_frameexit = "PALLENE_FRAMEEXIT();"
end
frameexit = "PALLENE_FRAMEEXIT();"
end

if slots_needed > 0 then
Expand All @@ -490,7 +491,6 @@ function Coder:pallene_entry_point_definition(f_id)
table.insert(prologue, self:savestack())
table.insert(prologue, "/**/")


for v_id = #arg_types + 1, #func.vars do
-- To avoid -Wmaybe-uninitialized warnings we have to initialize our local variables of type
-- "Any". Nils and Booleans only set the type tag of the TValue and leave the "._value"
Expand All @@ -502,22 +502,44 @@ function Coder:pallene_entry_point_definition(f_id)
table.insert(prologue, decl..initializer..";"..comment)
end

local body = self:generate_cmd(func, func.body)
local body = self:generate_blocks(func)

local ret_mult = ""
local ret = ""
if #func.ret_vars > 0 then
-- We assign the dsts from right to left, in order to match Lua's semantics when a
-- destination variable appears more than once in the LHS. For example, in `x,x = f()`.
-- For a more in-depth discussion, see the implementation of ast.Stat.Assign in to_ir.lua
local returns = {}
for i = #func.ret_vars, 2, -1 do
local var = self:c_var(func.ret_vars[i])
table.insert(returns,
util.render([[ *$reti = $v; ]], { reti = self:c_ret_var(i), v = var }))
end
ret_mult = table.concat(returns, "\n")

local var1 = self:c_var(func.ret_vars[1])
ret = "return " .. var1 .. ";"
end

return (util.render([[
${name_comment}
${fun_decl} {
${prologue}
/**/
${body}
${void_fe}
${ret_mult}
${frameexit}
${ret}
}
]], {
name_comment = C.comment(name_comment),
fun_decl = self:pallene_entry_point_declaration(f_id),
prologue = table.concat(prologue, "\n"),
body = body,
void_fe = void_frameexit
ret_mult = ret_mult,
frameexit = frameexit,
ret = ret,
}))
end

Expand Down Expand Up @@ -725,7 +747,7 @@ function Coder:init_upvalues()

-- String Literals
for _, func in ipairs(self.module.functions) do
for cmd in ir.iter(func.body) do
for cmd in ir.iter(func.blocks) do
gabrielsferre marked this conversation as resolved.
Show resolved Hide resolved
for _, v in ipairs(ir.get_srcs(cmd)) do
if v._tag == "ir.Value.String" then
local str = v.value
Expand Down Expand Up @@ -929,7 +951,7 @@ function Coder:init_gc()

for _, func in ipairs(self.module.functions) do
local max = 0
for cmd in ir.iter(func.body) do
for cmd in ir.iter(func.blocks) do
if cmd._tag == "ir.Cmd.CallDyn" then
-- Although in the end the fn call leaves only ndst items in
-- the stack, we actually need ndst+1 to appease the apicheck
Expand Down Expand Up @@ -1652,118 +1674,54 @@ gen_cmd["Nop"] = function(self, _cmd, _func)
return ""
end

gen_cmd["Seq"] = function(self, cmd, func)
local out = {}
for _, c in ipairs(cmd.cmds) do
table.insert(out, self:generate_cmd(func, c))
end
return table.concat(out, "\n")
end

gen_cmd["Return"] = function(self, cmd)
local frameexit = ""
if self.flags.use_traceback then
frameexit = "PALLENE_FRAMEEXIT();"
end

if #cmd.srcs == 0 then
return util.render([[ ${fexit}
return; ]], { fexit = frameexit })
else
-- We assign the dsts from right to left, in order to match Lua's semantics when a
-- destination variable appears more than once in the LHS. For example, in `x,x = f()`.
-- For a more in-depth discussion, see the implementation of ast.Stat.Assign in to_ir.lua
local returns = {}
for i = #cmd.srcs, 2, -1 do
local src = self:c_value(cmd.srcs[i])
table.insert(returns,
util.render([[ *$reti = $v; ]], { reti = self:c_ret_var(i), v = src }))
end
local src1 = self:c_value(cmd.srcs[1])
table.insert(returns, util.render([[ ${fexit}
return $v; ]], { fexit = frameexit, v = src1 }))
return table.concat(returns, "\n")
end
end

gen_cmd["Break"] = function(self, _cmd, _func)
return [[ break; ]]
end

gen_cmd["If"] = function(self, cmd, func)
local condition = self:c_value(cmd.src_condition)
local then_ = self:generate_cmd(func, cmd.then_)
local else_ = self:generate_cmd(func, cmd.else_)

local A = (then_ ~= "")
local B = (else_ ~= "")

local tmpl
if A and (not B) then
tmpl = [[
if ($condition) {
${then_}
}
]]
elseif (not A) and B then
tmpl = [[
if (!$condition) {
${else_}
}
]]
gen_cmd["InitFor"] = function(self, cmd, func)
gabrielsferre marked this conversation as resolved.
Show resolved Hide resolved
local typ = func.vars[cmd.dst_i].typ
local macro
if typ._tag == "types.T.Integer" then
macro = "PALLENE_INT_INIT_FOR_LOOP"
elseif typ._tag == "types.T.Float" then
macro = "PALLENE_FLT_INIT_FOR_LOOP"
else
tmpl = [[
if ($condition) {
${then_}
} else {
${else_}
}
]]
tagged_union.error(typ._tag)
end

return util.render(tmpl, {
condition = condition,
then_ = then_,
else_ = else_,
})
end

gen_cmd["Loop"] = function(self, cmd, func)
local body = self:generate_cmd(func, cmd.body)
return (util.render([[
while (1) {
${body}
}
${macro}($i, $cond, $iter, $count, $start, $limit, $step)
]], {
body = body
macro = macro,
i = self:c_var(cmd.dst_i),
cond = self:c_var(cmd.dst_cond),
iter = self:c_var(cmd.dst_iter),
count = self:c_var(cmd.dst_count),
start = self:c_value(cmd.src_start),
limit = self:c_value(cmd.src_limit),
step = self:c_value(cmd.src_step),
}))
end

gen_cmd["For"] = function(self, cmd, func)
local typ = func.vars[cmd.dst].typ
gen_cmd["IterFor"] = function(self, cmd, func)
local typ = func.vars[cmd.dst_i].typ
gabrielsferre marked this conversation as resolved.
Show resolved Hide resolved

local macro
if typ._tag == "types.T.Integer" then
macro = "PALLENE_INT_FOR_LOOP"
macro = "PALLENE_INT_ITER_FOR_LOOP"
elseif typ._tag == "types.T.Float" then
macro = "PALLENE_FLT_FOR_LOOP"
macro = "PALLENE_FLT_ITER_FOR_LOOP"
else
tagged_union.error(typ._tag)
end

return (util.render([[
${macro}_BEGIN($x, $start, $limit, $step)
{
$body
}
${macro}_END
${macro}($i, $cond, $iter, $count, $start, $limit, $step)
]], {
macro = macro,
x = self:c_var(cmd.dst),
i = self:c_var(cmd.dst_i),
cond = self:c_var(cmd.dst_cond),
iter = self:c_var(cmd.dst_iter),
count = self:c_var(cmd.dst_count),
start = self:c_value(cmd.src_start),
limit = self:c_value(cmd.src_limit),
step = self:c_value(cmd.src_step),
body = self:generate_cmd(func, cmd.body)
}))
end

Expand All @@ -1772,6 +1730,31 @@ gen_cmd["CheckGC"] = function(self, cmd, func)
update_stack_top = self:update_stack_top(func, cmd) })
end

function Coder:generate_blocks(func)
local out = ""
for i,block in ipairs(func.blocks) do
-- putting a "(void)0" at the label so compiler doesn't complain about dangling labels
local content = self:c_label(i) .. ":(void)0;\n"
for _,cmd in ipairs(block.cmds) do
content = content .. self:generate_cmd(func, cmd) .. "\n"
end
local jump_cond = ""
if block.jmp_false then
jump_cond = util.render("if(!($v)) {goto $l;}\n", {
v = self:c_value(block.jmp_false.src_condition),
l = self:c_label(block.jmp_false.target),
})
end
local jump = ""
if block.next and block.next ~= i + 1 then
jump = "goto " .. self:c_label(block.next) .. ";\n"
end
content = content .. jump_cond .. jump
out = out .. content
end
return out
end

gabrielsferre marked this conversation as resolved.
Show resolved Hide resolved
function Coder:generate_cmd(func, cmd)
assert(tagged_union.typename(cmd._tag) == "ir.Cmd")
local name = tagged_union.consname(cmd._tag)
Expand Down
35 changes: 19 additions & 16 deletions src/pallene/constant_propagation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function constant_propagation.run(module)
-- pass takes care of variables that are used before being initialized.
local f_data = assert(data_of_func[f_id])

for cmd in ir.iter(func.body) do
for cmd in ir.iter(func.blocks) do
local tag = cmd._tag
if tag == "ir.Cmd.Move" then
local id = cmd.dst
Expand Down Expand Up @@ -123,7 +123,7 @@ function constant_propagation.run(module)
local f_data = assert(data_of_func[f_id])
local n_writes = f_data.n_writes_of_locvar

for cmd in ir.iter(func.body) do
for cmd in ir.iter(func.blocks) do
local tag = cmd._tag
if tag == "ir.Cmd.InitUpvalues" then
local next_f = assert(data_of_func[cmd.f_id])
Expand Down Expand Up @@ -159,7 +159,7 @@ function constant_propagation.run(module)
-- x1 <- 20
-- }
--
for cmd in ir.iter(func.body) do
for cmd in ir.iter(func.blocks) do
local tag = cmd._tag
if tag == "ir.Cmd.InitUpvalues" then
local next_f = assert(data_of_func[cmd.f_id])
Expand All @@ -178,7 +178,7 @@ function constant_propagation.run(module)
--

for _, func in ipairs(module.functions) do
for cmd in ir.iter(func.body) do
for cmd in ir.iter(func.blocks) do
if cmd._tag == "ir.Cmd.InitUpvalues" then
local next_f = assert(data_of_func[cmd.f_id])
local new_u_id = next_f.new_upvalue_id
Expand Down Expand Up @@ -243,21 +243,24 @@ function constant_propagation.run(module)
func.f_id_of_upvalue = new_f_id_of_upvalue
end

func.body = ir.map_cmd(func.body, function(cmd)
local inputs = ir.get_value_field_names(cmd)
for _, src_field in ipairs(inputs.src) do
cmd[src_field] = updated_value(f_data, cmd[src_field])
end
for _,block in ipairs(func.blocks) do
for _,cmd in ipairs(block.cmds) do
local inputs = ir.get_value_field_names(cmd)
for _, src_field in ipairs(inputs.src) do
cmd[src_field] = updated_value(f_data, cmd[src_field])
end

for _, src_field in ipairs(inputs.srcs) do
local srcs = cmd[src_field]
for i, value in ipairs(srcs) do
srcs[i] = updated_value(f_data, value)
for _, src_field in ipairs(inputs.srcs) do
local srcs = cmd[src_field]
for i, value in ipairs(srcs) do
srcs[i] = updated_value(f_data, value)
end
end
end

return false
end)
if block.jmp_false then
block.jmp_false.src_condition = updated_value(f_data, block.jmp_false.src_condition)
end
end
end

ir.clean_all(module)
Expand Down
2 changes: 1 addition & 1 deletion src/pallene/gc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ local gc = {}

function gc.compute_stack_slots(func)

local flat_cmds = ir.flatten_cmd(func.body)
local flat_cmds = ir.flatten_cmd(func.blocks)

-- 1) Compute approximated live intervals for GC variables defined by the function. Function
-- parameters are only counted if they are redefined, since their original value was already
Expand Down
Loading
Loading