mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 19:22:37 +00:00
Add pragmas to funC for precise control of computation order (#589)
* FunC pragmas: allow-post-modification and compute-asm-ltr * Warn if #pragma is enabled only in included files * Add tests for new pragmas * Add special ops for "allow-post-modification" only when needed * Update FunC version to 0.4.1 * Allow empty inlines (#10) Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
parent
6b49d6a382
commit
653c88aa9d
8 changed files with 344 additions and 61 deletions
|
@ -875,9 +875,15 @@ x{EDFB} @Defop SAMEALTSAVE
|
|||
} dup : PREPARE : PREPAREDICT
|
||||
//
|
||||
// inline support
|
||||
{ dup sbits { @addop } {
|
||||
dup srefs 1- abort"exactly one reference expected in inline"
|
||||
ref@ CALLREF } cond
|
||||
{ dup sbits
|
||||
{ @addop }
|
||||
{
|
||||
dup srefs //
|
||||
{ ref@ CALLREF }
|
||||
{ drop }
|
||||
cond
|
||||
}
|
||||
cond
|
||||
} : INLINE
|
||||
//
|
||||
// throwing and handling exceptions
|
||||
|
|
78
crypto/func/auto-tests/tests/allow_post_modification.fc
Normal file
78
crypto/func/auto-tests/tests/allow_post_modification.fc
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma allow-post-modification;
|
||||
|
||||
forall X -> tuple unsafe_tuple(X x) asm "NOP";
|
||||
|
||||
(int, int) inc(int x, int y) {
|
||||
return (x + y, y * 10);
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) test_return(int x) method_id(11) {
|
||||
return (x, x~inc(x / 20), x, x = x * 2, x, x += 1, x);
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) test_assign(int x) method_id(12) {
|
||||
(int x1, int x2, int x3, int x4, int x5, int x6, int x7) = (x, x~inc(x / 20), x, x = x * 2, x, x += 1, x);
|
||||
return (x1, x2, x3, x4, x5, x6, x7);
|
||||
}
|
||||
|
||||
tuple test_tuple(int x) method_id(13) {
|
||||
tuple t = unsafe_tuple([x, x~inc(x / 20), x, x = x * 2, x, x += 1, x]);
|
||||
return t;
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) test_tuple_assign(int x) method_id(14) {
|
||||
[int x1, int x2, int x3, int x4, int x5, int x6, int x7] = [x, x~inc(x / 20), x, x = x * 2, x, x += 1, x];
|
||||
return (x1, x2, x3, x4, x5, x6, x7);
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) foo1(int x1, int x2, int x3, int x4, int x5, int x6, int x7) {
|
||||
return (x1, x2, x3, x4, x5, x6, x7);
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) test_call_1(int x) method_id(15) {
|
||||
return foo1(x, x~inc(x / 20), x, x = x * 2, x, x += 1, x);
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) foo2(int x1, int x2, (int, int, int, int) x3456, int x7) {
|
||||
(int x3, int x4, int x5, int x6) = x3456;
|
||||
return (x1, x2, x3, x4, x5, x6, x7);
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) test_call_2(int x) method_id(16) {
|
||||
return foo2(x, x~inc(x / 20), (x, x = x * 2, x, x += 1), x);
|
||||
}
|
||||
|
||||
(int, int, int, int, int, int, int) asm_func(int x1, int x2, int x3, int x4, int x5, int x6, int x7) asm
|
||||
(x4 x5 x6 x7 x1 x2 x3 -> 0 1 2 3 4 5 6) "NOP";
|
||||
|
||||
(int, int, int, int, int, int, int) test_call_asm_old(int x) method_id(17) {
|
||||
return asm_func(x, x += 1, x, x, x~inc(x / 20), x, x = x * 2);
|
||||
}
|
||||
|
||||
#pragma compute-asm-ltr;
|
||||
|
||||
(int, int, int, int, int, int, int) test_call_asm_new(int x) method_id(18) {
|
||||
return asm_func(x, x~inc(x / 20), x, x = x * 2, x, x += 1, x);
|
||||
}
|
||||
|
||||
global int xx;
|
||||
(int, int, int, int, int, int, int) test_global(int x) method_id(19) {
|
||||
xx = x;
|
||||
return (xx, xx~inc(xx / 20), xx, xx = xx * 2, xx, xx += 1, xx);
|
||||
}
|
||||
|
||||
() main() {
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 11 | 100 | 100 50 105 210 210 211 211
|
||||
TESTCASE | 12 | 100 | 100 50 105 210 210 211 211
|
||||
TESTCASE | 13 | 100 | [ 100 50 105 210 210 211 211 ]
|
||||
TESTCASE | 14 | 100 | 100 50 105 210 210 211 211
|
||||
TESTCASE | 15 | 100 | 100 50 105 210 210 211 211
|
||||
TESTCASE | 16 | 100 | 100 50 105 210 210 211 211
|
||||
TESTCASE | 17 | 100 | 100 50 105 210 210 211 211
|
||||
TESTCASE | 18 | 100 | 210 210 211 211 100 50 105
|
||||
TESTCASE | 19 | 100 | 100 50 105 210 210 211 211
|
||||
-}
|
113
crypto/func/auto-tests/tests/asm_arg_order.fc
Normal file
113
crypto/func/auto-tests/tests/asm_arg_order.fc
Normal file
|
@ -0,0 +1,113 @@
|
|||
tuple empty_tuple() asm "NIL";
|
||||
forall X -> (tuple, ()) tpush(tuple t, X x) asm "TPUSH";
|
||||
|
||||
tuple asm_func_1(int x, int y, int z) asm "3 TUPLE";
|
||||
tuple asm_func_2(int x, int y, int z) asm (z y x -> 0) "3 TUPLE";
|
||||
tuple asm_func_3(int x, int y, int z) asm (y z x -> 0) "3 TUPLE";
|
||||
tuple asm_func_4(int a, (int, (int, int)) b, int c) asm (b a c -> 0) "5 TUPLE";
|
||||
|
||||
(tuple, ()) asm_func_modify(tuple a, int b, int c) asm (c b a -> 0) "SWAP TPUSH SWAP TPUSH";
|
||||
|
||||
global tuple t;
|
||||
|
||||
int foo(int x) {
|
||||
t~tpush(x);
|
||||
return x * 10;
|
||||
}
|
||||
|
||||
(tuple, tuple) test_old_1() method_id(11) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = asm_func_1(foo(11), foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_old_2() method_id(12) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = asm_func_2(foo(11), foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_old_3() method_id(13) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = asm_func_3(foo(11), foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_old_4() method_id(14) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = empty_tuple();
|
||||
;; This actually computes left-to-right even without compute-asm-ltr
|
||||
tuple t2 = asm_func_4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_old_modify() method_id(15) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = empty_tuple();
|
||||
t2~asm_func_modify(foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_old_dot() method_id(16) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = foo(11).asm_func_3(foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
#pragma compute-asm-ltr;
|
||||
|
||||
(tuple, tuple) test_new_1() method_id(21) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = asm_func_1(foo(11), foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_new_2() method_id(22) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = asm_func_2(foo(11), foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_new_3() method_id(23) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = asm_func_3(foo(11), foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_new_4() method_id(24) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = asm_func_4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_new_modify() method_id(25) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = empty_tuple();
|
||||
t2~asm_func_modify(foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
(tuple, tuple) test_new_dot() method_id(26) {
|
||||
t = empty_tuple();
|
||||
tuple t2 = foo(11).asm_func_3(foo(22), foo(33));
|
||||
return (t, t2);
|
||||
}
|
||||
|
||||
() main() {
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 11 | | [ 11 22 33 ] [ 110 220 330 ]
|
||||
TESTCASE | 12 | | [ 33 22 11 ] [ 330 220 110 ]
|
||||
TESTCASE | 13 | | [ 22 33 11 ] [ 220 330 110 ]
|
||||
TESTCASE | 14 | | [ 11 22 33 44 55 ] [ 220 330 440 110 550 ]
|
||||
TESTCASE | 15 | | [ 33 22 ] [ 220 330 ]
|
||||
TESTCASE | 16 | | [ 22 33 11 ] [ 220 330 110 ]
|
||||
TESTCASE | 21 | | [ 11 22 33 ] [ 110 220 330 ]
|
||||
TESTCASE | 22 | | [ 11 22 33 ] [ 330 220 110 ]
|
||||
TESTCASE | 23 | | [ 11 22 33 ] [ 220 330 110 ]
|
||||
TESTCASE | 24 | | [ 11 22 33 44 55 ] [ 220 330 440 110 550 ]
|
||||
TESTCASE | 25 | | [ 22 33 ] [ 220 330 ]
|
||||
TESTCASE | 26 | | [ 11 22 33 ] [ 220 330 110 ]
|
||||
-}
|
|
@ -36,6 +36,8 @@ namespace funC {
|
|||
int verbosity, indent, opt_level = 2;
|
||||
bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble;
|
||||
bool interactive = false;
|
||||
GlobalPragma pragma_allow_post_modification{"allow-post-modification"};
|
||||
GlobalPragma pragma_compute_asm_ltr{"compute-asm-ltr"};
|
||||
std::string generated_from, boc_output_filename;
|
||||
|
||||
/*
|
||||
|
@ -194,7 +196,7 @@ int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, st
|
|||
int ok = 0, proc = 0;
|
||||
try {
|
||||
for (auto src : sources) {
|
||||
ok += funC::parse_source_file(src.c_str());
|
||||
ok += funC::parse_source_file(src.c_str(), {}, true);
|
||||
proc++;
|
||||
}
|
||||
if (funC::interactive) {
|
||||
|
@ -208,6 +210,8 @@ int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, st
|
|||
if (!proc) {
|
||||
throw src::Fatal{"no source files, no output"};
|
||||
}
|
||||
pragma_allow_post_modification.check_enable_in_libs();
|
||||
pragma_compute_asm_ltr.check_enable_in_libs();
|
||||
return funC::generate_output(outs, errs);
|
||||
} catch (src::Fatal& fatal) {
|
||||
errs << "fatal: " << fatal << std::endl;
|
||||
|
|
|
@ -39,7 +39,7 @@ extern std::string generated_from;
|
|||
|
||||
constexpr int optimize_depth = 20;
|
||||
|
||||
const std::string func_version{"0.4.0"};
|
||||
const std::string func_version{"0.4.1"};
|
||||
|
||||
enum Keyword {
|
||||
_Eof = -1,
|
||||
|
@ -306,7 +306,7 @@ struct TmpVar {
|
|||
sym_idx_t name;
|
||||
int coord;
|
||||
std::unique_ptr<SrcLocation> where;
|
||||
size_t modify_forbidden = 0;
|
||||
std::vector<std::function<void(const SrcLocation &)>> on_modification;
|
||||
TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type = 0, SymDef* sym = 0, const SrcLocation* loc = 0);
|
||||
void show(std::ostream& os, int omit_idx = 0) const;
|
||||
void dump(std::ostream& os) const;
|
||||
|
@ -681,6 +681,7 @@ typedef std::vector<FormalArg> FormalArgList;
|
|||
struct AsmOpList;
|
||||
|
||||
struct CodeBlob {
|
||||
enum { _AllowPostModification = 1, _ComputeAsmLtr = 2 };
|
||||
int var_cnt, in_var_cnt, op_cnt;
|
||||
TypeExpr* ret_type;
|
||||
std::string name;
|
||||
|
@ -689,6 +690,7 @@ struct CodeBlob {
|
|||
std::unique_ptr<Op> ops;
|
||||
std::unique_ptr<Op>* cur_ops;
|
||||
std::stack<std::unique_ptr<Op>*> cur_ops_stack;
|
||||
int flags = 0;
|
||||
CodeBlob(TypeExpr* ret = nullptr) : var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), cur_ops(&ops) {
|
||||
}
|
||||
template <typename... Args>
|
||||
|
@ -729,19 +731,9 @@ struct CodeBlob {
|
|||
void generate_code(AsmOpList& out_list, int mode = 0);
|
||||
void generate_code(std::ostream& os, int mode = 0, int indent = 0);
|
||||
|
||||
void mark_modify_forbidden(var_idx_t idx) {
|
||||
++vars.at(idx).modify_forbidden;
|
||||
}
|
||||
|
||||
void unmark_modify_forbidden(var_idx_t idx) {
|
||||
assert(vars.at(idx).modify_forbidden > 0);
|
||||
--vars.at(idx).modify_forbidden;
|
||||
}
|
||||
|
||||
void check_modify_forbidden(var_idx_t idx, const SrcLocation& here) const {
|
||||
if (vars.at(idx).modify_forbidden) {
|
||||
throw src::ParseError{here, PSTRING() << "Modifying local variable " << vars[idx].to_string()
|
||||
<< " after using it in the same expression"};
|
||||
void on_var_modification(var_idx_t idx, const SrcLocation& here) const {
|
||||
for (auto& f : vars.at(idx).on_modification) {
|
||||
f(here);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -855,7 +847,7 @@ extern std::vector<SymDef*> glob_func, glob_vars;
|
|||
|
||||
// defined in parse-func.cpp
|
||||
bool parse_source(std::istream* is, const src::FileDescr* fdescr);
|
||||
bool parse_source_file(const char* filename, src::Lexem lex = {});
|
||||
bool parse_source_file(const char* filename, src::Lexem lex = {}, bool is_main = false);
|
||||
bool parse_source_stdin();
|
||||
|
||||
extern std::stack<src::SrcLocation> inclusion_locations;
|
||||
|
@ -1700,6 +1692,41 @@ extern int verbosity, indent, opt_level;
|
|||
extern bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble, interactive;
|
||||
extern std::string generated_from, boc_output_filename;
|
||||
|
||||
class GlobalPragma {
|
||||
public:
|
||||
explicit GlobalPragma(std::string name) : name_(std::move(name)) {
|
||||
}
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
bool enabled() const {
|
||||
return enabled_;
|
||||
}
|
||||
void enable(SrcLocation loc) {
|
||||
enabled_ = true;
|
||||
locs_.push_back(std::move(loc));
|
||||
}
|
||||
void check_enable_in_libs() {
|
||||
if (locs_.empty()) {
|
||||
return;
|
||||
}
|
||||
for (const SrcLocation& loc : locs_) {
|
||||
if (loc.fdescr->is_main) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
locs_[0].show_warning(PSTRING() << "#pragma " << name_
|
||||
<< " is enabled in included libraries, it may change the behavior of your code. "
|
||||
<< "Add this #pragma to the main source file to suppress this warning.");
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
bool enabled_ = false;
|
||||
std::vector<SrcLocation> locs_;
|
||||
};
|
||||
extern GlobalPragma pragma_allow_post_modification, pragma_compute_asm_ltr;
|
||||
|
||||
/*
|
||||
*
|
||||
* OUTPUT CODE GENERATOR
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include <numeric>
|
||||
#include "func.h"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
@ -255,13 +256,75 @@ std::vector<var_idx_t> Expr::pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rh
|
|||
std::vector<std::pair<SymDef*, var_idx_t>> globs;
|
||||
auto left = lhs->pre_compile(code, &globs);
|
||||
for (var_idx_t v : left) {
|
||||
code.check_modify_forbidden(v, here);
|
||||
code.on_var_modification(v, here);
|
||||
}
|
||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||
add_set_globs(code, globs, here);
|
||||
return right;
|
||||
}
|
||||
|
||||
std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *> args, CodeBlob &code,
|
||||
std::vector<std::pair<SymDef*, var_idx_t>> *lval_globs,
|
||||
std::vector<int> arg_order) {
|
||||
if (arg_order.empty()) {
|
||||
arg_order.resize(args.size());
|
||||
std::iota(arg_order.begin(), arg_order.end(), 0);
|
||||
}
|
||||
assert(args.size() == arg_order.size());
|
||||
std::vector<std::vector<var_idx_t>> res_lists(args.size());
|
||||
|
||||
struct ModifiedVar {
|
||||
size_t i, j;
|
||||
Op* op;
|
||||
};
|
||||
auto modified_vars = std::make_shared<std::vector<ModifiedVar>>();
|
||||
for (size_t i : arg_order) {
|
||||
res_lists[i] = args[i]->pre_compile(code, lval_globs);
|
||||
for (size_t j = 0; j < res_lists[i].size(); ++j) {
|
||||
TmpVar& var = code.vars.at(res_lists[i][j]);
|
||||
if (code.flags & CodeBlob::_AllowPostModification) {
|
||||
if (!lval_globs && (var.cls & TmpVar::_Named)) {
|
||||
Op *op = &code.emplace_back(nullptr, Op::_Let, std::vector<var_idx_t>(), std::vector<var_idx_t>());
|
||||
op->flags |= Op::_Disabled;
|
||||
var.on_modification.push_back([modified_vars, i, j, op, done = false](const SrcLocation &here) mutable {
|
||||
if (!done) {
|
||||
done = true;
|
||||
modified_vars->push_back({i, j, op});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var.on_modification.push_back([](const SrcLocation &) {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
var.on_modification.push_back([name = var.to_string()](const SrcLocation &here) {
|
||||
throw src::ParseError{here, PSTRING() << "Modifying local variable " << name
|
||||
<< " after using it in the same expression"};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& list : res_lists) {
|
||||
for (var_idx_t v : list) {
|
||||
assert(!code.vars.at(v).on_modification.empty());
|
||||
code.vars.at(v).on_modification.pop_back();
|
||||
}
|
||||
}
|
||||
for (const ModifiedVar &m : *modified_vars) {
|
||||
var_idx_t& v = res_lists[m.i][m.j];
|
||||
var_idx_t v2 = code.create_tmp_var(code.vars[v].v_type, code.vars[v].where.get());
|
||||
m.op->left = {v2};
|
||||
m.op->right = {v};
|
||||
m.op->flags &= ~Op::_Disabled;
|
||||
v = v2;
|
||||
}
|
||||
std::vector<var_idx_t> res;
|
||||
for (const auto& list : res_lists) {
|
||||
res.insert(res.end(), list.cbegin(), list.cend());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>* lval_globs) const {
|
||||
if (lval_globs && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _TypeApply || cls == _GlobVar)) {
|
||||
std::cerr << "lvalue expression constructor is " << cls << std::endl;
|
||||
|
@ -269,46 +332,17 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
|
|||
}
|
||||
switch (cls) {
|
||||
case _Tensor: {
|
||||
std::vector<var_idx_t> res;
|
||||
for (const auto& x : args) {
|
||||
auto add = x->pre_compile(code, lval_globs);
|
||||
for (var_idx_t v : add) {
|
||||
code.mark_modify_forbidden(v);
|
||||
}
|
||||
res.insert(res.end(), add.cbegin(), add.cend());
|
||||
}
|
||||
for (var_idx_t v : res) {
|
||||
code.unmark_modify_forbidden(v);
|
||||
}
|
||||
return res;
|
||||
return pre_compile_tensor(args, code, lval_globs, {});
|
||||
}
|
||||
case _Apply: {
|
||||
assert(sym);
|
||||
std::vector<var_idx_t> res;
|
||||
auto func = dynamic_cast<SymValFunc*>(sym->value);
|
||||
if (func && func->arg_order.size() == args.size()) {
|
||||
std::vector<var_idx_t> res;
|
||||
if (func && func->arg_order.size() == args.size() && !(code.flags & CodeBlob::_ComputeAsmLtr)) {
|
||||
//std::cerr << "!!! reordering " << args.size() << " arguments of " << sym->name() << std::endl;
|
||||
std::vector<std::vector<var_idx_t>> add_list(args.size());
|
||||
for (int i : func->arg_order) {
|
||||
add_list[i] = args[i]->pre_compile(code);
|
||||
for (var_idx_t v : add_list[i]) {
|
||||
code.mark_modify_forbidden(v);
|
||||
}
|
||||
}
|
||||
for (const auto& add : add_list) {
|
||||
res.insert(res.end(), add.cbegin(), add.cend());
|
||||
}
|
||||
res = pre_compile_tensor(args, code, lval_globs, func->arg_order);
|
||||
} else {
|
||||
for (const auto& x : args) {
|
||||
auto add = x->pre_compile(code);
|
||||
for (var_idx_t v : add) {
|
||||
code.mark_modify_forbidden(v);
|
||||
}
|
||||
res.insert(res.end(), add.cbegin(), add.cend());
|
||||
}
|
||||
}
|
||||
for (var_idx_t v : res) {
|
||||
code.unmark_modify_forbidden(v);
|
||||
res = pre_compile_tensor(args, code, lval_globs, {});
|
||||
}
|
||||
auto rvect = new_tmp_vect(code);
|
||||
auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), sym);
|
||||
|
@ -371,7 +405,7 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
|
|||
auto left = args[0]->pre_compile(code, lval_globs);
|
||||
left.push_back(rvect[0]);
|
||||
for (var_idx_t v : left) {
|
||||
code.check_modify_forbidden(v, here);
|
||||
code.on_var_modification(v, here);
|
||||
}
|
||||
code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
|
||||
add_set_globs(code, local_globs, here);
|
||||
|
|
|
@ -261,6 +261,12 @@ void parse_const_decl(Lexer& lex) {
|
|||
}
|
||||
lex.next();
|
||||
CodeBlob code;
|
||||
if (pragma_allow_post_modification.enabled()) {
|
||||
code.flags |= CodeBlob::_AllowPostModification;
|
||||
}
|
||||
if (pragma_compute_asm_ltr.enabled()) {
|
||||
code.flags |= CodeBlob::_ComputeAsmLtr;
|
||||
}
|
||||
// Handles processing and resolution of literals and consts
|
||||
auto x = parse_expr(lex, code, false); // also does lex.next() !
|
||||
if (x->flags != Expr::_IsRvalue) {
|
||||
|
@ -1210,6 +1216,12 @@ blk_fl::val parse_stmt(Lexer& lex, CodeBlob& code) {
|
|||
CodeBlob* parse_func_body(Lexer& lex, FormalArgList arg_list, TypeExpr* ret_type) {
|
||||
lex.expect('{');
|
||||
CodeBlob* blob = new CodeBlob{ret_type};
|
||||
if (pragma_allow_post_modification.enabled()) {
|
||||
blob->flags |= CodeBlob::_AllowPostModification;
|
||||
}
|
||||
if (pragma_compute_asm_ltr.enabled()) {
|
||||
blob->flags |= CodeBlob::_ComputeAsmLtr;
|
||||
}
|
||||
blob->import_params(std::move(arg_list));
|
||||
blk_fl::val res = blk_fl::init;
|
||||
bool warned = false;
|
||||
|
@ -1676,6 +1688,10 @@ void parse_pragma(Lexer& lex) {
|
|||
}
|
||||
func_ver_test = lex.cur().str;
|
||||
lex.next();
|
||||
} else if (pragma_name == pragma_allow_post_modification.name()) {
|
||||
pragma_allow_post_modification.enable(lex.cur().loc);
|
||||
} else if (pragma_name == pragma_compute_asm_ltr.name()) {
|
||||
pragma_compute_asm_ltr.enable(lex.cur().loc);
|
||||
} else {
|
||||
lex.cur().error(std::string{"unknown pragma `"} + pragma_name + "`");
|
||||
}
|
||||
|
@ -1684,7 +1700,7 @@ void parse_pragma(Lexer& lex) {
|
|||
|
||||
std::vector<const src::FileDescr*> source_fdescr;
|
||||
|
||||
std::vector<std::string> source_files;
|
||||
std::map<std::string, src::FileDescr*> source_files;
|
||||
std::stack<src::SrcLocation> inclusion_locations;
|
||||
|
||||
void parse_include(Lexer& lex, const src::FileDescr* fdescr) {
|
||||
|
@ -1700,7 +1716,7 @@ void parse_include(Lexer& lex, const src::FileDescr* fdescr) {
|
|||
}
|
||||
lex.next();
|
||||
lex.expect(';');
|
||||
if (!parse_source_file(val.c_str(), include)) {
|
||||
if (!parse_source_file(val.c_str(), include, false)) {
|
||||
include.error(std::string{"failed parsing included file `"} + val + "`");
|
||||
}
|
||||
}
|
||||
|
@ -1724,7 +1740,7 @@ bool parse_source(std::istream* is, src::FileDescr* fdescr) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool parse_source_file(const char* filename, src::Lexem lex) {
|
||||
bool parse_source_file(const char* filename, src::Lexem lex, bool is_main) {
|
||||
if (!filename || !*filename) {
|
||||
auto msg = "source file name is an empty string";
|
||||
if (lex.tp) {
|
||||
|
@ -1741,7 +1757,9 @@ bool parse_source_file(const char* filename, src::Lexem lex) {
|
|||
return false;
|
||||
}
|
||||
std::string real_filename = path_res.move_as_ok();
|
||||
if (std::count(source_files.begin(), source_files.end(), real_filename)) {
|
||||
auto it = source_files.find(real_filename);
|
||||
if (it != source_files.end()) {
|
||||
it->second->is_main |= is_main;
|
||||
if (verbosity >= 2) {
|
||||
if (lex.tp) {
|
||||
lex.loc.show_warning(std::string{"skipping file "} + real_filename + " because it was already included");
|
||||
|
@ -1755,8 +1773,9 @@ bool parse_source_file(const char* filename, src::Lexem lex) {
|
|||
funC::generated_from += std::string{"incl:"};
|
||||
}
|
||||
funC::generated_from += std::string{"`"} + filename + "` ";
|
||||
source_files.push_back(real_filename);
|
||||
src::FileDescr* cur_source = new src::FileDescr{filename};
|
||||
source_files[real_filename] = cur_source;
|
||||
cur_source->is_main = is_main;
|
||||
source_fdescr.push_back(cur_source);
|
||||
std::ifstream ifs{filename};
|
||||
if (ifs.fail()) {
|
||||
|
@ -1775,6 +1794,7 @@ bool parse_source_file(const char* filename, src::Lexem lex) {
|
|||
|
||||
bool parse_source_stdin() {
|
||||
src::FileDescr* cur_source = new src::FileDescr{"stdin", true};
|
||||
cur_source->is_main = true;
|
||||
source_fdescr.push_back(cur_source);
|
||||
return parse_source(&std::cin, cur_source);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ struct FileDescr {
|
|||
std::string text;
|
||||
std::vector<long> line_offs;
|
||||
bool is_stdin;
|
||||
bool is_main = false;
|
||||
FileDescr(std::string _fname, bool _stdin = false) : filename(std::move(_fname)), is_stdin(_stdin) {
|
||||
}
|
||||
const char* push_line(std::string new_line);
|
||||
|
|
Loading…
Reference in a new issue