Skip to content

Commit 6f60419

Browse files
committed
[Tolk] Prevent exponential explosion while evaluating constants
1 parent 5756e73 commit 6f60419

File tree

3 files changed

+163
-12
lines changed

3 files changed

+163
-12
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
const __precisionZeros = 1000000000000000000; // 18 zeros
2+
const __base0 = 1000000043929018416; // 4x every year (3600 * (24 * 365 + 6) seconds)
3+
4+
const __base1 = __base0 * __base0 / __precisionZeros;
5+
const __base2 = __base1 * __base1 / __precisionZeros;
6+
const __base3 = __base2 * __base2 / __precisionZeros;
7+
const __base4 = __base3 * __base3 / __precisionZeros;
8+
const __base5 = __base4 * __base4 / __precisionZeros;
9+
const __base6 = __base5 * __base5 / __precisionZeros;
10+
const __base7 = __base6 * __base6 / __precisionZeros;
11+
const __base8 = __base7 * __base7 / __precisionZeros;
12+
const __base9 = __base8 * __base8 / __precisionZeros;
13+
const __base10 = __base9 * __base9 / __precisionZeros;
14+
const __base11 = __base10 * __base10 / __precisionZeros;
15+
const __base12 = __base11 * __base11 / __precisionZeros;
16+
const __base13 = __base12 * __base12 / __precisionZeros;
17+
const __base14 = __base13 * __base13 / __precisionZeros;
18+
const __base15 = __base14 * __base14 / __precisionZeros;
19+
const __base16 = __base15 * __base15 / __precisionZeros;
20+
const __base17 = __base16 * __base16 / __precisionZeros;
21+
const __base18 = __base17 * __base17 / __precisionZeros;
22+
const __base19 = __base18 * __base18 / __precisionZeros;
23+
const __base20 = __base19 * __base19 / __precisionZeros;
24+
const __base21 = __base20 * __base20 / __precisionZeros;
25+
const __base22 = __base21 * __base21 / __precisionZeros;
26+
const __base23 = __base22 * __base22 / __precisionZeros;
27+
const __base24 = __base23 * __base23 / __precisionZeros;
28+
const __base25 = __base24 * __base24 / __precisionZeros;
29+
const __base26 = __base25 * __base25 / __precisionZeros;
30+
const __base27 = __base26 * __base26 / __precisionZeros;
31+
const __base28 = __base27 * __base27 / __precisionZeros;
32+
const __base29 = __base28 * __base28 / __precisionZeros;
33+
const __base30 = __base29 * __base29 / __precisionZeros;
34+
35+
const ERROR_EXP_TOO_LARGE = 10970;
36+
37+
@method_id(101)
38+
fun test1(value: int, exp: int): int {
39+
assert (exp <= 0x7fffffff) throw ERROR_EXP_TOO_LARGE;
40+
41+
if ((exp & 0x00000001) != 0) { value = mulDivFloor(value, __base0, __precisionZeros); }
42+
if ((exp & 0x00000002) != 0) { value = mulDivFloor(value, __base1, __precisionZeros); }
43+
if ((exp & 0x00000004) != 0) { value = mulDivFloor(value, __base2, __precisionZeros); }
44+
if ((exp & 0x00000008) != 0) { value = mulDivFloor(value, __base3, __precisionZeros); }
45+
46+
if ((exp & 0x00000010) != 0) { value = mulDivFloor(value, __base4, __precisionZeros); }
47+
if ((exp & 0x00000020) != 0) { value = mulDivFloor(value, __base5, __precisionZeros); }
48+
if ((exp & 0x00000040) != 0) { value = mulDivFloor(value, __base6, __precisionZeros); }
49+
if ((exp & 0x00000080) != 0) { value = mulDivFloor(value, __base7, __precisionZeros); }
50+
51+
if ((exp & 0x00000100) != 0) { value = mulDivFloor(value, __base8, __precisionZeros); }
52+
if ((exp & 0x00000200) != 0) { value = mulDivFloor(value, __base9, __precisionZeros); }
53+
if ((exp & 0x00000400) != 0) { value = mulDivFloor(value, __base10, __precisionZeros); }
54+
if ((exp & 0x00000800) != 0) { value = mulDivFloor(value, __base11, __precisionZeros); }
55+
56+
if ((exp & 0x00001000) != 0) { value = mulDivFloor(value, __base12, __precisionZeros); }
57+
if ((exp & 0x00002000) != 0) { value = mulDivFloor(value, __base13, __precisionZeros); }
58+
if ((exp & 0x00004000) != 0) { value = mulDivFloor(value, __base14, __precisionZeros); }
59+
if ((exp & 0x00008000) != 0) { value = mulDivFloor(value, __base15, __precisionZeros); }
60+
61+
if ((exp & 0x00010000) != 0) { value = mulDivFloor(value, __base16, __precisionZeros); }
62+
if ((exp & 0x00020000) != 0) { value = mulDivFloor(value, __base17, __precisionZeros); }
63+
if ((exp & 0x00040000) != 0) { value = mulDivFloor(value, __base18, __precisionZeros); }
64+
if ((exp & 0x00080000) != 0) { value = mulDivFloor(value, __base19, __precisionZeros); }
65+
66+
if ((exp & 0x00100000) != 0) { value = mulDivFloor(value, __base20, __precisionZeros); }
67+
if ((exp & 0x00200000) != 0) { value = mulDivFloor(value, __base21, __precisionZeros); }
68+
if ((exp & 0x00400000) != 0) { value = mulDivFloor(value, __base22, __precisionZeros); }
69+
if ((exp & 0x00800000) != 0) { value = mulDivFloor(value, __base23, __precisionZeros); }
70+
71+
if ((exp & 0x01000000) != 0) { value = mulDivFloor(value, __base24, __precisionZeros); }
72+
if ((exp & 0x02000000) != 0) { value = mulDivFloor(value, __base25, __precisionZeros); }
73+
if ((exp & 0x04000000) != 0) { value = mulDivFloor(value, __base26, __precisionZeros); }
74+
if ((exp & 0x08000000) != 0) { value = mulDivFloor(value, __base27, __precisionZeros); }
75+
76+
if ((exp & 0x10000000) != 0) { value = mulDivFloor(value, __base28, __precisionZeros); }
77+
if ((exp & 0x20000000) != 0) { value = mulDivFloor(value, __base29, __precisionZeros); }
78+
if ((exp & 0x40000000) != 0) { value = mulDivFloor(value, __base30, __precisionZeros); }
79+
80+
return value;
81+
}
82+
83+
@method_id(102)
84+
fun test2(g1: bool) {
85+
if (g1) {
86+
return __base30;
87+
}
88+
return __base30;
89+
}
90+
91+
fun main() {
92+
return 0x7fffffff;
93+
}
94+
95+
/**
96+
@testcase | 101 | 12345678901234567890 0 | 12345678901234567890
97+
@testcase | 101 | 12345678901234567890 2147483647 | 1152091301147391687695287037940761486473052236056853112522947
98+
@testcase | 102 | 0 | 305482242253527345283975278648594775678
99+
@testcase | 102 | -1 | 305482242253527345283975278648594775678
100+
101+
@fif_codegen
102+
"""
103+
test2() PROC:<{
104+
IFJMP:<{
105+
305482242253527345283975278648594775678 PUSHINT // '62
106+
}>
107+
305482242253527345283975278648594775678 PUSHINT // '124
108+
}>
109+
"""
110+
*/

tolk/pipe-ast-to-legacy.cpp

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,15 @@ const LazyVariableLoadedState* CodeBlob::get_lazy_variable(AnyExprV v) const {
383383
return nullptr;
384384
}
385385

386+
const CachedConstValueAtCodegen* CodeBlob::get_cached_const(GlobalConstPtr const_ref) const {
387+
for (const CachedConstValueAtCodegen& c : cached_consts) {
388+
if (c.const_ref == const_ref) {
389+
return &c;
390+
}
391+
}
392+
return nullptr;
393+
}
394+
386395

387396
// given `{some_expr}!`, return some_expr
388397
static AnyExprV unwrap_not_null_operator(AnyExprV v) {
@@ -1111,6 +1120,38 @@ std::vector<var_idx_t> transition_to_target_type(std::vector<var_idx_t>&& rvect,
11111120

11121121

11131122
std::vector<var_idx_t> pre_compile_symbol(SrcLocation loc, const Symbol* sym, CodeBlob& code, LValContext* lval_ctx) {
1123+
// referencing a local variable (not its declaration, but its usage)
1124+
if (LocalVarPtr var_ref = sym->try_as<LocalVarPtr>()) {
1125+
#ifdef TOLK_DEBUG
1126+
tolk_assert(static_cast<int>(var_ref->ir_idx.size()) == var_ref->declared_type->get_width_on_stack());
1127+
#endif
1128+
return var_ref->ir_idx;
1129+
}
1130+
1131+
// referencing a global constant, embed its init_value directly (so, it will be evaluated to a const val later)
1132+
if (GlobalConstPtr const_ref = sym->try_as<GlobalConstPtr>()) {
1133+
tolk_assert(lval_ctx == nullptr);
1134+
// when evaluating `a1` in `const a2 = a1 + a1`, cache a1's IR vars to prevent exponential explosion
1135+
if (code.inside_evaluating_constant) {
1136+
if (const CachedConstValueAtCodegen* cached = code.get_cached_const(const_ref)) {
1137+
return cached->ir_idx;
1138+
}
1139+
std::vector<var_idx_t> ir_sub_const = pre_compile_expr(const_ref->init_value, code, const_ref->declared_type);
1140+
code.cached_consts.emplace_back(CachedConstValueAtCodegen{const_ref, ir_sub_const});
1141+
return ir_sub_const;
1142+
}
1143+
// just referencing `a1` in a function body
1144+
// (note that `a1` occurred 2 times has 2 different ir_idx, caching above is done only within another const)
1145+
ASTAuxData* aux_data = new AuxData_ForceFiftLocation(loc);
1146+
auto v_force_loc = createV<ast_artificial_aux_vertex>(loc, const_ref->init_value, aux_data, const_ref->inferred_type);
1147+
code.inside_evaluating_constant = true;
1148+
std::vector<var_idx_t> ir_const = pre_compile_expr(v_force_loc, code, const_ref->declared_type);
1149+
code.inside_evaluating_constant = false;
1150+
code.cached_consts.clear();
1151+
return ir_const;
1152+
}
1153+
1154+
// referencing a global variable, copy it to a local tmp var
11141155
if (GlobalVarPtr glob_ref = sym->try_as<GlobalVarPtr>()) {
11151156
// handle `globalVar = rhs` / `mutate globalVar`
11161157
if (lval_ctx && !lval_ctx->is_rval_inside_lval()) {
@@ -1127,23 +1168,14 @@ std::vector<var_idx_t> pre_compile_symbol(SrcLocation loc, const Symbol* sym, Co
11271168
}
11281169
return local_ir_idx;
11291170
}
1130-
if (GlobalConstPtr const_ref = sym->try_as<GlobalConstPtr>()) {
1131-
tolk_assert(lval_ctx == nullptr);
1132-
ASTAuxData* aux_data = new AuxData_ForceFiftLocation(loc);
1133-
auto v_force_loc = createV<ast_artificial_aux_vertex>(loc, const_ref->init_value, aux_data, const_ref->inferred_type);
1134-
return pre_compile_expr(v_force_loc, code, const_ref->declared_type);
1135-
}
1171+
1172+
// referencing a function (not calling it! using as a callback, works similar to a global var)
11361173
if (FunctionPtr fun_ref = sym->try_as<FunctionPtr>()) {
11371174
std::vector<var_idx_t> rvect = code.create_tmp_var(fun_ref->inferred_full_type, loc, "(glob-var-fun)");
11381175
code.emplace_back(loc, Op::_GlobVar, rvect, std::vector<var_idx_t>{}, fun_ref);
11391176
return rvect;
11401177
}
1141-
if (LocalVarPtr var_ref = sym->try_as<LocalVarPtr>()) {
1142-
#ifdef TOLK_DEBUG
1143-
tolk_assert(static_cast<int>(var_ref->ir_idx.size()) == var_ref->declared_type->get_width_on_stack());
1144-
#endif
1145-
return var_ref->ir_idx;
1146-
}
1178+
11471179
throw Fatal("pre_compile_symbol");
11481180
}
11491181

tolk/tolk.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,14 +1068,22 @@ struct LazyVarRefAtCodegen {
10681068
: var_ref(var_ref), var_state(var_state) {}
10691069
};
10701070

1071+
// CachedConstValueAtCodegen is used for a map [some_const => '5]
1072+
struct CachedConstValueAtCodegen {
1073+
GlobalConstPtr const_ref;
1074+
std::vector<var_idx_t> ir_idx;
1075+
};
1076+
10711077
struct CodeBlob {
10721078
int var_cnt, in_var_cnt;
10731079
FunctionPtr fun_ref;
10741080
std::string name;
10751081
SrcLocation forced_loc;
10761082
std::vector<TmpVar> vars;
10771083
std::vector<LazyVarRefAtCodegen> lazy_variables;
1084+
std::vector<CachedConstValueAtCodegen> cached_consts;
10781085
std::vector<var_idx_t>* inline_rvect_out = nullptr;
1086+
bool inside_evaluating_constant = false;
10791087
bool inlining_before_immediate_return = false;
10801088
std::unique_ptr<Op> ops;
10811089
std::unique_ptr<Op>* cur_ops;
@@ -1130,6 +1138,7 @@ struct CodeBlob {
11301138
}
11311139
const LazyVariableLoadedState* get_lazy_variable(LocalVarPtr var_ref) const;
11321140
const LazyVariableLoadedState* get_lazy_variable(AnyExprV v) const;
1141+
const CachedConstValueAtCodegen* get_cached_const(GlobalConstPtr const_ref) const;
11331142
void prune_unreachable_code();
11341143
void fwd_analyze();
11351144
void mark_noreturn();

0 commit comments

Comments
 (0)