Skip to content

Commit 5840882

Browse files
authored
Merge pull request #652 from pallene-lang/renormarr-command
Introducing new IR commands RenormArr and Nop
2 parents c59c2a5 + 430f191 commit 5840882

File tree

6 files changed

+276
-36
lines changed

6 files changed

+276
-36
lines changed

build

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
eval "$(luarocks path)"
3+
export PATH="$(pwd)/src/bin/:$PATH"
4+
export LUA_PATH="$(pwd)/src/?.lua;$LUA_PATH"
5+
export CFLAGS='-O2 -std=c99 -Wall -Werror -Wundef -Wpedantic -Wno-unused'
6+
7+
function count_stack_saves() {
8+
file="$1"
9+
grep -Eo 'set[^\n]*\( *L *, *s2v\( *base' "$file" | wc -l
10+
}
11+
12+
function is_in() {
13+
element="$1"
14+
shift
15+
list="$*"
16+
found=false
17+
for e in $list; do
18+
[ "$e" = "$element" ] && found=true && break
19+
done
20+
$found
21+
}
22+
23+
if [ "$1" = "test" ]; then
24+
shift
25+
./run-tests "$@"
26+
elif [ "$1" = "testgc" ]; then
27+
shift
28+
EXTRACFLAGS="-DHARDMEMTESTS" ./run-tests "$@"
29+
else
30+
31+
file="$1"
32+
shift
33+
34+
lint_cmd="./run-lint -q"
35+
run_cmd="lua lua_$file.lua"
36+
flags=''
37+
is_in c $* && flags="$flags --emit-c" && run_cmd=true
38+
is_in ir $* && flags="$flags --print-ir"
39+
is_in O0 $* && flags="$flags -O0"
40+
is_in trace $* && flags="$flags --use-traceback"
41+
is_in gc $* && CFLAGS='-O4 -DHARDMEMTESTS'
42+
is_in nolint $* && lint_cmd=true
43+
is_in norun $* && run_cmd=true
44+
45+
$lint_cmd &&
46+
pallenec $flags "$file".pln &&
47+
$run_cmd
48+
fi

src/pallene/coder.lua

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function Coder:init(module, modname, filename, flags)
8888
self.record_coders[typ] = RecordCoder.new(self, typ)
8989
end
9090

91-
self.gc = {} -- (see gc.compute_stack_slots)
91+
self.gc = {} -- { func => gc_info } see gc.compute_gc_info
9292
self.max_lua_call_stack_usage = {} -- func => integer
9393
self:init_gc()
9494
end
@@ -1265,6 +1265,20 @@ gen_cmd["NewArr"] = function(self, args)
12651265
}))
12661266
end
12671267

1268+
gen_cmd["RenormArr"] = function(self, args)
1269+
local arr = self:c_value(args.cmd.src_arr)
1270+
local i = self:c_value(args.cmd.src_i)
1271+
local line = C.integer(args.cmd.loc.line)
1272+
1273+
return (util.render([[
1274+
pallene_renormalize_array(L, $arr, $i, PALLENE_SOURCE_FILE, $line);
1275+
]], {
1276+
arr = arr,
1277+
i = i,
1278+
line = line,
1279+
}))
1280+
end
1281+
12681282
gen_cmd["GetArr"] = function(self, args)
12691283
local dst = self:c_var(args.cmd.dst)
12701284
local arr = self:c_value(args.cmd.src_arr)
@@ -1274,7 +1288,6 @@ gen_cmd["GetArr"] = function(self, args)
12741288

12751289
return (util.render([[
12761290
{
1277-
pallene_renormalize_array(L, $arr, $i, PALLENE_SOURCE_FILE, $line);
12781291
TValue *slot = &$arr->array[$i - 1];
12791292
$get_slot
12801293
}
@@ -1295,7 +1308,6 @@ gen_cmd["SetArr"] = function(self, args)
12951308
local line = C.integer(args.cmd.loc.line)
12961309
return (util.render([[
12971310
{
1298-
pallene_renormalize_array(L, $arr, $i, PALLENE_SOURCE_FILE, $line);
12991311
TValue *slot = &$arr->array[$i - 1];
13001312
${set_heap_slot}
13011313
}
@@ -1768,6 +1780,10 @@ gen_cmd["JmpIf"] = function(self, args)
17681780
})
17691781
end
17701782

1783+
gen_cmd["Nop"] = function()
1784+
return ""
1785+
end
1786+
17711787
gen_cmd["CheckGC"] = function(self, args)
17721788
return util.render([[ luaC_condGC(L, ${update_stack_top}, (void)0); ]], {
17731789
update_stack_top = self:update_stack_top(args.position) })

src/pallene/gc.lua

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ local function compute_stack_slots(func)
8888
return gk
8989
end
9090

91-
local flow_info = flow.FlowInfo(flow.Order.Backwards, compute_gen_kill, init_start)
91+
local flow_info = flow.FlowInfo(
92+
flow.Order.Backwards, compute_gen_kill, init_start)
9293
local sets_list = flow.flow_analysis(func.blocks, flow_info)
9394

9495
-- 2) Find which GC'd variables are live at each GC spot in the program and
@@ -160,12 +161,116 @@ local function compute_stack_slots(func)
160161
return live_gc_vars, max_frame_size, slot_of_variable
161162
end
162163

164+
local function Definition(block_i, cmd_i, var_i)
165+
return {
166+
block_i = block_i,
167+
cmd_i = cmd_i,
168+
var_i = var_i,
169+
}
170+
end
171+
172+
local function make_definition_list(func)
173+
local def_list = {} -- { Definition }
174+
local cmd_def_map = {} -- { block_id => { cmd_id => {definition_id} } }
175+
local var_def_map = {} -- { var_id => {definition_id}? }
176+
for var_i, var in ipairs(func.vars) do
177+
if types.is_gc(var.typ) then
178+
var_def_map[var_i] = {}
179+
else
180+
var_def_map[var_i] = false
181+
end
182+
end
183+
for block_i, block in ipairs(func.blocks) do
184+
local block_map = {}
185+
cmd_def_map[block_i] = block_map
186+
for cmd_i, cmd in ipairs(block.cmds) do
187+
local cmd_map = {}
188+
block_map[cmd_i] = cmd_map
189+
for _, dst in ipairs(ir.get_dsts(cmd)) do
190+
local typ = func.vars[dst].typ
191+
if types.is_gc(typ) then
192+
local def = Definition(block_i,cmd_i,dst)
193+
table.insert(def_list, def)
194+
local def_id = #def_list
195+
table.insert(cmd_map, def_id)
196+
197+
local var_defs = var_def_map[dst]
198+
table.insert(var_defs, def_id)
199+
end
200+
end
201+
end
202+
end
203+
return def_list, cmd_def_map, var_def_map
204+
end
205+
206+
local function compute_vars_to_mirror(func)
207+
208+
-- 1) Register definitions of GC'd variables
209+
local def_list, cmd_def_map, var_def_map = make_definition_list(func)
210+
211+
-- 2) Find reaching definitions for each basic block
212+
local function init_start(_start_set, _block_index)
213+
end
214+
215+
local function compute_gen_kill(block_i, cmd_i)
216+
local cmd = func.blocks[block_i].cmds[cmd_i]
217+
local gk = flow.GenKill()
218+
for _, dst in ipairs(ir.get_dsts(cmd)) do
219+
local typ = func.vars[dst].typ
220+
if types.is_gc(typ) then
221+
local var_defs = var_def_map[dst]
222+
for _, def_id in ipairs(var_defs) do
223+
flow.kill_value(gk, def_id)
224+
end
225+
end
226+
end
227+
local current_defs = cmd_def_map[block_i][cmd_i]
228+
if current_defs then
229+
for _, def in ipairs(current_defs) do
230+
flow.gen_value(gk, def)
231+
end
232+
end
233+
return gk
234+
end
235+
236+
local flow_info = flow.FlowInfo(
237+
flow.Order.Forward, compute_gen_kill, init_start)
238+
local sets_list = flow.flow_analysis(func.blocks, flow_info)
239+
240+
-- 3) Find which definitions reach commands that might call the GC, that is, which definitions
241+
-- writes have to be mirroed to the stack
242+
local vars_to_mirror = {} -- { block_id => { cmd_id => set of var_i } }
243+
for block_i, block in ipairs(func.blocks) do
244+
local block_defs = {}
245+
vars_to_mirror[block_i] = block_defs
246+
for cmd_i = 1, #block.cmds do
247+
block_defs[cmd_i] = {}
248+
end
249+
end
250+
for block_i, block in ipairs(func.blocks) do
251+
local defs_block = sets_list[block_i]
252+
for cmd_i, cmd in ipairs(block.cmds) do
253+
flow.update_set(defs_block, flow_info, block_i, cmd_i)
254+
if cmd_uses_gc(cmd) then
255+
for def_i, _ in pairs(defs_block) do
256+
local def = def_list[def_i]
257+
vars_to_mirror[def.block_i][def.cmd_i][def.var_i] = true
258+
end
259+
end
260+
end
261+
end
262+
263+
return vars_to_mirror
264+
end
265+
163266
function gc.compute_gc_info(func)
164267
local live_gc_vars, max_frame_size, slot_of_variable = compute_stack_slots(func)
268+
local vars_to_mirror = compute_vars_to_mirror(func)
165269
return {
166270
live_gc_vars = live_gc_vars,
167271
max_frame_size = max_frame_size,
168272
slot_of_variable = slot_of_variable,
273+
vars_to_mirror = vars_to_mirror,
169274
}
170275
end
171276

src/pallene/ir.lua

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ function ir.VarDecl(name, typ)
4242
}
4343
end
4444

45+
function ir.ForLoop()
46+
return {
47+
step_is_positive = false, -- boolean
48+
prep_block_id = false, -- block_id
49+
body_first_block_id = false, -- block_id
50+
body_last_block_id = false, -- block_id
51+
iteration_variable_id = false, -- v_id
52+
limit_value = false, -- ir.Value
53+
loc = false, -- Location
54+
}
55+
end
56+
4557
function ir.Function(loc, name, typ)
4658
return {
4759
loc = loc, -- Location
@@ -53,6 +65,7 @@ function ir.Function(loc, name, typ)
5365
f_id_of_local = {}, -- { v_id => integer }
5466
blocks = {}, -- { ir.BasicBlock }
5567
ret_vars = {}, -- { v_id }, list of return variables
68+
for_loops = {}, -- { ir.ForLoop }
5669
}
5770
end
5871

@@ -125,6 +138,15 @@ define_union("Value", {
125138
Upvalue = {"id"},
126139
})
127140

141+
function ir.is_constant(value)
142+
local tag = value._tag
143+
return tag == "ir.Value.Nil" or
144+
tag == "ir.Value.Bool" or
145+
tag == "ir.Value.Integer" or
146+
tag == "ir.Value.Float" or
147+
tag == "ir.Value.String"
148+
end
149+
128150
-- define_union("Cmd"
129151
local ir_cmd_constructors = {
130152
-- [IMPORTANT] Please use this naming convention:
@@ -148,6 +170,7 @@ local ir_cmd_constructors = {
148170

149171
-- Arrays
150172
NewArr = {"loc", "dst", "src_size"},
173+
RenormArr = {"loc", "src_arr", "src_i"}, -- renormalize array
151174

152175
GetArr = {"loc", "dst_typ", "dst", "src_arr", "src_i"},
153176
SetArr = {"loc", "src_typ", "src_arr", "src_i", "src_v"},
@@ -209,6 +232,8 @@ local ir_cmd_constructors = {
209232
Jmp = {"target"},
210233
JmpIf = {"loc", "src_cond", "target_true", "target_false"},
211234

235+
Nop = {}, -- does nothing
236+
212237
-- Garbage Collection (appears after memory allocations)
213238
CheckGC = {},
214239
}
@@ -277,13 +302,13 @@ function ir.BasicBlock()
277302
}
278303
end
279304

280-
local function is_jump(cmd)
305+
function ir.is_jump(cmd)
281306
return cmd._tag == "ir.Cmd.Jmp" or cmd._tag == "ir.Cmd.JmpIf"
282307
end
283308

284309
function ir.get_jump(block)
285310
local cmd = block.cmds[#block.cmds]
286-
if not cmd or not is_jump(cmd) then
311+
if not cmd or not ir.is_jump(cmd) then
287312
return false
288313
end
289314
return cmd
@@ -394,29 +419,41 @@ function ir.get_predecessor_depth_search_topological_sort(predecessor_list)
394419
return reverse_order
395420
end
396421

397-
-- Iterate over the cmds of basic blocks using a naive ordering.
398-
function ir.iter(block_list)
399-
local function go()
400-
for _,block in ipairs(block_list) do
401-
for _, cmd in ipairs(block.cmds) do
402-
coroutine.yield(cmd)
422+
-- Inserts a block into a given index on a function's block list and updates block index references
423+
-- accordingly.
424+
function ir.insert_block(func, new_block, index)
425+
assert(index > 0)
426+
for _, block in ipairs(func.blocks) do
427+
for _, cmd in ipairs(block.cmds) do
428+
if cmd._tag == "ir.Cmd.Jmp" then
429+
cmd.target = cmd.target + (cmd.target >= index and 1 or 0)
430+
elseif cmd._tag == "ir.Cmd.JmpIf" then
431+
cmd.target_true = cmd.target_true + (cmd.target_true >= index and 1 or 0)
432+
cmd.target_false = cmd.target_false + (cmd.target_false >= index and 1 or 0)
403433
end
404434
end
405435
end
406-
return coroutine.wrap(function() go() end)
407-
end
408436

409-
function ir.flatten_cmd(block_list)
410-
local res = {}
411-
for _,block in ipairs(block_list) do
412-
for _,cmd in ipairs(block.cmds) do
413-
table.insert(res, cmd)
437+
for _, loop in ipairs(func.for_loops) do
438+
loop.prep_block_id = loop.prep_block_id + (loop.prep_block_id >= index and 1 or 0)
439+
loop.body_first_block_id = loop.body_first_block_id + (loop.body_first_block_id >= index and 1 or 0)
440+
loop.body_last_block_id = loop.body_last_block_id + (loop.body_last_block_id >= index and 1 or 0)
441+
end
442+
443+
local offset = index - 1
444+
for _, cmd in ipairs(new_block.cmds) do
445+
if cmd._tag == "ir.Cmd.Jmp" then
446+
cmd.target = cmd.target + offset
447+
elseif cmd._tag == "ir.Cmd.JmpIf" then
448+
cmd.target_true = cmd.target_true + offset
449+
cmd.target_false = cmd.target_false + offset
414450
end
415451
end
416-
return res
452+
453+
table.insert(func.blocks, index, new_block)
417454
end
418455

419-
-- Remove jumps that are never taken
456+
-- Remove jumps that are never taken.
420457
function ir.clean(func)
421458
for _, block in ipairs(func.blocks) do
422459
local jump = ir.get_jump(block)

src/pallene/print_ir.lua

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ local function Cmd(cmd)
122122
local tag = cmd._tag
123123

124124
local lhs
125-
if tag == "ir.Cmd.Nop" then
126-
return "nop"
127-
elseif tag == "ir.Cmd.Return" then
125+
if tag == "ir.Cmd.Return" then
128126
return "return " .. comma_concat(Vals(cmd.srcs))
129127
elseif tag == "ir.Cmd.SetArr" then lhs = Bracket(cmd.src_arr, cmd.src_i)
130128
elseif tag == "ir.Cmd.SetTable" then lhs = Bracket(cmd.src_tab, cmd.src_k)
@@ -154,6 +152,7 @@ local function Cmd(cmd)
154152
elseif tag == "ir.Cmd.JmpIf" then
155153
rhs = "jmpIf " .. Val(cmd.src_cond) .. ", " .. cmd.target_true .. ", " .. cmd.target_false
156154
elseif tag == "ir.Cmd.Jmp" then rhs = "jmp " .. cmd.target
155+
elseif tag == "ir.Cmd.Nop" then rhs = "nop"
157156
elseif tagged_union.typename(cmd._tag) == "ir.Cmd" then
158157
local name = tagged_union.consname(cmd._tag)
159158
rhs = Call(name, Vals(ir.get_srcs(cmd)))

0 commit comments

Comments
 (0)