Skip to content

Commit 27ee768

Browse files
authored
Merge pull request #165 from certik/rt1
Implement proper intrinsic functions handling
2 parents e8aec8d + 83141ce commit 27ee768

8 files changed

+214
-10
lines changed

integration_tests/test_builtin_abs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ def test_abs():
66
assert abs(x) == 5.5
77
x = -5.5
88
assert abs(x) == 5.5
9+
assert abs(5.5) == 5.5
10+
assert abs(-5.5) == 5.5
911

1012

1113
test_abs()

src/libasr/asr_utils.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,18 @@ static inline bool is_intrinsic_function(const ASR::Function_t *fn) {
368368
return false;
369369
}
370370

371+
// Returns true if the Function is intrinsic, otherwise false
372+
// This version uses the `intrinsic` member of `Module`, so it
373+
// should be used instead of is_intrinsic_function
374+
static inline bool is_intrinsic_function2(const ASR::Function_t *fn) {
375+
ASR::symbol_t *sym = (ASR::symbol_t*)fn;
376+
ASR::Module_t *m = get_sym_module0(sym);
377+
if (m != nullptr) {
378+
if (m->m_intrinsic) return true;
379+
}
380+
return false;
381+
}
382+
371383
// Returns true if all arguments have a `value`
372384
static inline bool all_args_have_value(const Vec<ASR::expr_t*> &args) {
373385
for (auto &a : args) {

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <lpython/utils.h>
2323
#include <lpython/semantics/semantic_exception.h>
2424
#include <lpython/python_serialization.h>
25+
#include <lpython/semantics/python_comptime_eval.h>
2526

2627

2728
namespace LFortran::Python {
@@ -147,11 +148,14 @@ class CommonVisitor : public AST::BaseVisitor<Derived> {
147148
ASR::asr_t *tmp;
148149
Allocator &al;
149150
SymbolTable *current_scope;
151+
// The current_module contains the current module that is being visited;
152+
// this is used to append to the module dependencies if needed
150153
ASR::Module_t *current_module = nullptr;
151154
Vec<char *> current_module_dependencies;
152155
// True for the main module, false for every other one
153156
// The main module is stored directly in TranslationUnit, other modules are Modules
154157
bool main_module;
158+
PythonIntrinsicProcedures intrinsic_procedures;
155159

156160
CommonVisitor(Allocator &al, SymbolTable *symbol_table,
157161
diag::Diagnostics &diagnostics, bool main_module)
@@ -178,6 +182,72 @@ class CommonVisitor : public AST::BaseVisitor<Derived> {
178182
return ASR::make_Var_t(al, loc, v);
179183
}
180184

185+
ASR::symbol_t* resolve_intrinsic_function(const Location &loc, const std::string &remote_sym) {
186+
LFORTRAN_ASSERT(intrinsic_procedures.is_intrinsic(remote_sym))
187+
std::string module_name = intrinsic_procedures.get_module(remote_sym, loc);
188+
189+
SymbolTable *tu_symtab = ASRUtils::get_tu_symtab(current_scope);
190+
std::string rl_path = get_runtime_library_dir();
191+
bool ltypes;
192+
ASR::Module_t *m = load_module(al, tu_symtab, module_name,
193+
loc, true, rl_path,
194+
ltypes,
195+
[&](const std::string &msg, const Location &loc) { throw SemanticError(msg, loc); }
196+
);
197+
LFORTRAN_ASSERT(!ltypes)
198+
199+
ASR::symbol_t *t = m->m_symtab->resolve_symbol(remote_sym);
200+
if (!t) {
201+
throw SemanticError("The symbol '" + remote_sym
202+
+ "' not found in the module '" + module_name + "'",
203+
loc);
204+
} else if (! (ASR::is_a<ASR::GenericProcedure_t>(*t)
205+
|| ASR::is_a<ASR::Function_t>(*t)
206+
|| ASR::is_a<ASR::Subroutine_t>(*t))) {
207+
throw SemanticError("The symbol '" + remote_sym
208+
+ "' found in the module '" + module_name + "', "
209+
+ "but it is not a function, subroutine or a generic procedure.",
210+
loc);
211+
}
212+
char *fn_name = ASRUtils::symbol_name(t);
213+
ASR::asr_t *fn = ASR::make_ExternalSymbol_t(
214+
al, t->base.loc,
215+
/* a_symtab */ current_scope,
216+
/* a_name */ fn_name,
217+
t,
218+
m->m_name, nullptr, 0, fn_name,
219+
ASR::accessType::Private
220+
);
221+
std::string sym = fn_name;
222+
223+
current_scope->scope[sym] = ASR::down_cast<ASR::symbol_t>(fn);
224+
ASR::symbol_t *v = ASR::down_cast<ASR::symbol_t>(fn);
225+
226+
// Now we need to add the module `m` with the intrinsic function
227+
// into the current module dependencies
228+
if (current_module) {
229+
// We are in body visitor, the module is already constructed
230+
// and available as current_module.
231+
// Add the module `m` to current module dependencies
232+
Vec<char*> vec;
233+
vec.from_pointer_n_copy(al, current_module->m_dependencies,
234+
current_module->n_dependencies);
235+
if (!present(vec, m->m_name)) {
236+
vec.push_back(al, m->m_name);
237+
current_module->m_dependencies = vec.p;
238+
current_module->n_dependencies = vec.size();
239+
}
240+
} else {
241+
// We are in the symtab visitor or body visitor and we are
242+
// constructing a module, so current_module is not available yet
243+
// (the current_module_dependencies is not used in body visitor)
244+
if (!present(current_module_dependencies, m->m_name)) {
245+
current_module_dependencies.push_back(al, m->m_name);
246+
}
247+
}
248+
return v;
249+
}
250+
181251
// Convert Python AST type annotation to an ASR type
182252
// Examples:
183253
// i32, i64, f32, f64
@@ -1981,6 +2051,18 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
19812051

19822052

19832053
if (!s) {
2054+
if (intrinsic_procedures.is_intrinsic(call_name)) {
2055+
s = resolve_intrinsic_function(x.base.base.loc, call_name);
2056+
} else {
2057+
// TODO: We need to port all functions below to the intrinsic functions file
2058+
// Then we can uncomment this error message:
2059+
/*
2060+
throw SemanticError("The function '" + call_name + "' is not declared and not intrinsic",
2061+
x.base.base.loc);
2062+
}
2063+
if (false) {
2064+
*/
2065+
// This will all be removed once we port it to intrinsic functions
19842066
// Intrinsic functions
19852067
if (call_name == "size") {
19862068
// TODO: size should be part of ASR. That way
@@ -2481,21 +2563,22 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
24812563
throw SemanticError("Function '" + call_name + "' is not declared and not intrinsic",
24822564
x.base.base.loc);
24832565
}
2566+
} // end of "comment"
24842567
}
24852568

2486-
if (!s) {
2487-
throw SemanticError("Function '" + call_name + "' is not declared",
2488-
x.base.base.loc);
2489-
}
24902569
// handling ExternalSymbol
24912570
ASR::symbol_t *stemp = s;
24922571
s = ASRUtils::symbol_get_past_external(s);
24932572

24942573
if(ASR::is_a<ASR::Function_t>(*s)) {
24952574
ASR::Function_t *func = ASR::down_cast<ASR::Function_t>(s);
24962575
ASR::ttype_t *a_type = ASRUtils::expr_type(func->m_return_var);
2576+
ASR::expr_t *value = nullptr;
2577+
if (ASRUtils::is_intrinsic_function2(func)) {
2578+
value = intrinsic_procedures.comptime_eval(call_name, al, x.base.base.loc, args);
2579+
}
24972580
tmp = ASR::make_FunctionCall_t(al, x.base.base.loc, stemp,
2498-
nullptr, args.p, args.size(), nullptr, 0, a_type, nullptr, nullptr);
2581+
nullptr, args.p, args.size(), nullptr, 0, a_type, value, nullptr);
24992582
} else if(ASR::is_a<ASR::Subroutine_t>(*s)) {
25002583
tmp = ASR::make_SubroutineCall_t(al, x.base.base.loc, stemp,
25012584
nullptr, args.p, args.size(), nullptr);
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#ifndef LPYTHON_SEMANTICS_COMPTIME_EVAL_H
2+
#define LPYTHON_SEMANTICS_COMPTIME_EVAL_H
3+
4+
#include <complex>
5+
6+
#include <libasr/asr.h>
7+
#include <lpython/ast.h>
8+
#include <lpython/bigint.h>
9+
#include <libasr/string_utils.h>
10+
#include <lpython/utils.h>
11+
#include <lpython/semantics/semantic_exception.h>
12+
13+
namespace LFortran {
14+
15+
struct PythonIntrinsicProcedures {
16+
17+
const std::string m_builtin = "lpython_builtin";
18+
19+
typedef ASR::expr_t* (*comptime_eval_callback)(Allocator &, const Location &, Vec<ASR::expr_t*> &);
20+
// Table of intrinsics
21+
// The callback is only called if all arguments have compile time `value`
22+
// which is always one of the `Constant*` expression ASR nodes, so inside
23+
// the callback one can assume that.
24+
std::map<std::string, std::tuple<std::string, comptime_eval_callback>> comptime_eval_map;
25+
26+
PythonIntrinsicProcedures() {
27+
comptime_eval_map = {
28+
{"abs", {m_builtin, &eval_abs}},
29+
};
30+
}
31+
32+
// Return `true` if `name` is in the table of intrinsics
33+
bool is_intrinsic(std::string name) const {
34+
auto search = comptime_eval_map.find(name);
35+
if (search != comptime_eval_map.end()) {
36+
return true;
37+
} else {
38+
return false;
39+
}
40+
}
41+
42+
// Looks up `name` in the table of intrinsics and returns the corresponding
43+
// module name; Otherwise rises an exception
44+
std::string get_module(std::string name, const Location &loc) const {
45+
auto search = comptime_eval_map.find(name);
46+
if (search != comptime_eval_map.end()) {
47+
std::string module_name = std::get<0>(search->second);
48+
return module_name;
49+
} else {
50+
throw SemanticError("Function '" + name
51+
+ "' not found among intrinsic procedures",
52+
loc);
53+
}
54+
}
55+
56+
// Evaluates the intrinsic function `name` at compile time
57+
ASR::expr_t *comptime_eval(std::string name, Allocator &al, const Location &loc, Vec<ASR::expr_t*> &args) const {
58+
auto search = comptime_eval_map.find(name);
59+
if (search != comptime_eval_map.end()) {
60+
comptime_eval_callback cb = std::get<1>(search->second);
61+
Vec<ASR::expr_t*> arg_values = ASRUtils::get_arg_values(al, args);
62+
if (arg_values.size() != args.size()) {
63+
// Not all arguments have compile time values; we do not call the callback
64+
return nullptr;
65+
}
66+
return cb(al, loc, arg_values);
67+
} else {
68+
throw SemanticError("Intrinsic function '" + name
69+
+ "' compile time evaluation is not implemented yet",
70+
loc);
71+
}
72+
}
73+
74+
75+
static ASR::expr_t *eval_abs(Allocator &al, const Location &loc,
76+
Vec<ASR::expr_t*> &args
77+
) {
78+
LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args));
79+
if (args.size() != 1) {
80+
throw SemanticError("Intrinsic abs function accepts exactly 1 argument", loc);
81+
}
82+
ASR::expr_t* trig_arg = args[0];
83+
ASR::ttype_t* t = ASRUtils::expr_type(args[0]);
84+
if (ASR::is_a<ASR::Real_t>(*t)) {
85+
double rv = ASR::down_cast<ASR::ConstantReal_t>(trig_arg)->m_r;
86+
double val = std::abs(rv);
87+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantReal_t(al, loc, val, t));
88+
} else if (ASR::is_a<ASR::Integer_t>(*t)) {
89+
int64_t rv = ASR::down_cast<ASR::ConstantInteger_t>(trig_arg)->m_n;
90+
int64_t val = std::abs(rv);
91+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantInteger_t(al, loc, val, t));
92+
} else if (ASR::is_a<ASR::Complex_t>(*t)) {
93+
double re = ASR::down_cast<ASR::ConstantComplex_t>(trig_arg)->m_re;
94+
double im = ASR::down_cast<ASR::ConstantComplex_t>(trig_arg)->m_im;
95+
std::complex<double> x(re, im);
96+
double result = std::abs(x);
97+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantReal_t(al, loc, result, t));
98+
} else {
99+
throw SemanticError("Argument of the abs function must be Integer, Real or Complex", loc);
100+
}
101+
}
102+
103+
}; // ComptimeEval
104+
105+
} // namespace LFortran
106+
107+
#endif /* LPYTHON_SEMANTICS_COMPTIME_EVAL_H */

tests/reference/asr-constants1-5828e8a.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "asr-constants1-5828e8a.stdout",
9-
"stdout_hash": "82599c6a0114feef56f5e9198dd5cfbd06d87638efc14126af92a89e",
9+
"stdout_hash": "0a0d5acbd9b14c908f856d9d95d36e49e4cb2f5db735109a008af200",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

0 commit comments

Comments
 (0)