Skip to content

Commit 31ceefd

Browse files
committed
Allow improveIf of con/undet when only one constructor
1 parent f365d35 commit 31ceefd

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

src/comp/IExpand.hs

+36
Original file line numberDiff line numberDiff line change
@@ -4398,6 +4398,42 @@ improveIf f t cnd thn@(IAps chr@(ICon _ (ICPrim _ PrimChr)) ts1 [chr_thn])
43984398
let chrArgType = iGetType chr_thn
43994399
(e', _) <- improveIf f chrArgType cnd chr_thn chr_els
44004400
return (IAps chr ts1 [e'], True)
4401+
4402+
-- Push if improvement inside constructors when one arm is undefined
4403+
-- and the type has only one constructor
4404+
--
4405+
-- Further down, a general improveIf rule optimizes 'if c e _' to just 'e'.
4406+
-- But that can cause poor code generation for if-else chains returning
4407+
-- the constructors of a union type, so an earlier improveIf rule catches
4408+
-- that situation (before the general rule can apply).
4409+
-- However, if there is only one constructor, we do want an optimization to apply,
4410+
-- so we put that here, prior to the blocking rule.
4411+
--
4412+
improveIf f t cnd thn@(IAps (ICon i1 c1@(ICCon {})) ts1 es1)
4413+
els@(ICon i2 (ICUndet { iuKind = u }))
4414+
| numCon (conTagInfo c1) == 1
4415+
= do
4416+
when doTraceIf $ traceM ("improveIf ICCon/ICUndet triggered" ++ ppReadable (cnd,thn,els))
4417+
let realConType = itInst (iConType c1) ts1
4418+
(argTypes, _) = itGetArrows realConType
4419+
when (length argTypes /= length es1) $ internalError ("improveIf Con/Undet:" ++ ppReadable (argTypes, es1))
4420+
let mkUndet t = icUndetAt (getIdPosition i2) t u
4421+
(es', bs) <- mapAndUnzipM (\(t, e1) -> improveIf f t cnd e1 (mkUndet t)) (zip argTypes es1)
4422+
-- unambiguous improvement because the ICCon has propagated out
4423+
return ((IAps (ICon i1 c1) ts1 es'), True)
4424+
improveIf f t cnd thn@(ICon i1 (ICUndet { iuKind = u }))
4425+
els@(IAps (ICon i2 c2@(ICCon {})) ts2 es2)
4426+
| numCon (conTagInfo c2) == 1
4427+
= do
4428+
when doTraceIf $ traceM ("improveIf ICCon/ICUndet triggered" ++ ppReadable (cnd,thn,els))
4429+
let realConType = itInst (iConType c2) ts2
4430+
(argTypes, _) = itGetArrows realConType
4431+
when (length argTypes /= length es2) $ internalError ("improveIf Con/Undet:" ++ ppReadable (argTypes, es2))
4432+
let mkUndet t = icUndetAt (getIdPosition i1) t u
4433+
(es', bs) <- mapAndUnzipM (\(t, e2) -> improveIf f t cnd (mkUndet t) e2) (zip argTypes es2)
4434+
-- unambiguous improvement because the ICCon has propagated out
4435+
return ((IAps (ICon i2 c2) ts2 es'), True)
4436+
44014437
-- Do not "optimize" constructors against undefined values because this can remove
44024438
-- the conditions required to optimize chains of ifs like these:
44034439
-- if (x == 0) 0 else if (x == 1) 1 else ... back to just x
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// This example comes from GitHub Issue #742
2+
//
3+
// This is a simple example that should compile without a lot of
4+
// unfolding steps or heap space. However, if the evaluator is
5+
// missing an optimization for this:
6+
//
7+
// if (cond)
8+
// then Ctor e1 e2 ...
9+
// else _
10+
//
11+
// the compiler will take time and memory until eventually exiting
12+
// with an error message about max unfolding steps reached or
13+
// (if the max steps is increased) that stack space was exhausted.
14+
//
15+
// For better code generation of "pack of unpack" for union types, we
16+
// don't want the evaluator to optimize away the don't-care like this:
17+
//
18+
// ==> Ctor e1 e2 ...
19+
//
20+
// However, when the type only has one constructor, such as Vector in
21+
// this example, we can optimize the expression to this:
22+
//
23+
// ==> Ctor
24+
// (if (cond) then e1 else _)
25+
// (if (cond) then e2 else _)
26+
// ...
27+
//
28+
// With this optimization, the example will successfully compile
29+
// (quickly, without many unfolding steps or stack space usage).
30+
//
31+
32+
import Vector::*;
33+
34+
`define Q_SIZE 8
35+
typedef Bit#(2) T;
36+
37+
(*synthesize*)
38+
module mkImproveIf_ConUndet_OneCon ();
39+
40+
Vector#(`Q_SIZE, Reg#(T)) vec_data <- replicateM(mkRegU);
41+
Reg #(Maybe#(Vector#(`Q_SIZE, T))) rg_1 <- mkRegU;
42+
Reg #(Maybe#(Vector#(`Q_SIZE, T))) rg_2 <- mkRegU;
43+
Reg #(Vector#(`Q_SIZE, Bool)) rg_3 <- mkRegU;
44+
Reg #(Maybe#(Vector#(`Q_SIZE, T))) rg_4 <- mkRegU;
45+
Reg #(Vector#(`Q_SIZE, Bool)) rg_5 <- mkRegU;
46+
47+
rule r;
48+
Vector#(`Q_SIZE, T) tmp_data = readVReg (vec_data);
49+
50+
if (rg_1 matches tagged Valid .d1)
51+
tmp_data = d1;
52+
53+
if (rg_2 matches tagged Valid .d2)
54+
begin
55+
for (int i = 0 ; i< `Q_SIZE ;i = i+1)
56+
begin
57+
if (rg_3[i] == True)
58+
tmp_data[i] = d2[i];
59+
end
60+
end
61+
62+
if (rg_4 matches tagged Valid .d4)
63+
begin
64+
for (int i = 0 ; i< `Q_SIZE ;i = i+1)
65+
begin
66+
if (rg_5[i] == True)
67+
tmp_data[i] = d4[i];
68+
end
69+
end
70+
71+
writeVReg(vec_data,tmp_data);
72+
endrule
73+
74+
endmodule

testsuite/bsc.evaluator/opt/opt.exp

+10
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,13 @@ if { $vtest == 1 } {
6363
find_n_strings sysCompareSameExpr.v {rSLE} 1
6464
find_n_strings sysCompareSameExpr.v {rSGE} 1
6565
}
66+
67+
# Test that the evaluator has an 'improveIf' optimization for
68+
# ICCon/ICUndet when the type has only one constructor
69+
# (GitHub Issue #742)
70+
#
71+
# Without the optimization, there is excessive unfolding steps
72+
# and stack usage
73+
#
74+
compile_verilog_pass ImproveIf_ConUndet_OneCon.bsv {} \
75+
{-steps-max-intervals 10 -steps-warn-interval 100000 +RTS -K10m -RTS}

0 commit comments

Comments
 (0)