Factor out some parts of builtin handling and add declaration checking. (#3815)

In preparation for adding more builtins, factor out the handling of
builtin function kinds into separate files.

Add checking for builtin function signatures. The mechanism used here is
intended to provide a lot of flexibility for declaring generic builtin
functions and pretty arbitrary constraints on the types of parameters of
builtin functions. For now, these constraints are checked when the
builtin function is declared. The hope is that this will suffice, but if
not, it should be straightforward to switch to doing some of the
checking on call and share logic between the checks.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
This commit is contained in:
Richard Smith
2024-03-26 22:58:11 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins Carbon Infra Bot
parent 3763d0130a
commit a3d77d9b74
11 changed files with 402 additions and 156 deletions
+12 -34
View File
@@ -5,6 +5,7 @@
#include "toolchain/check/eval.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/sem_ir/builtin_function_kind.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/typed_insts.h"
@@ -286,7 +287,7 @@ static auto PerformAggregateIndex(Context& context, SemIR::Inst inst)
}
static auto PerformBuiltinCall(Context& context, SemIRLocation loc,
SemIR::Call call,
SemIR::Call /*call*/,
SemIR::BuiltinFunctionKind builtin_kind,
llvm::ArrayRef<SemIR::InstId> arg_ids,
Phase phase) -> SemIR::ConstantId {
@@ -298,20 +299,12 @@ static auto PerformBuiltinCall(Context& context, SemIRLocation loc,
if (phase != Phase::Template) {
break;
}
if (arg_ids.size() != 2) {
break;
}
auto lhs = context.insts().TryGetAs<SemIR::IntLiteral>(arg_ids[0]);
auto rhs = context.insts().TryGetAs<SemIR::IntLiteral>(arg_ids[1]);
// TODO: Move type checking to the point where we make the call.
if (!lhs || !rhs || lhs->type_id != rhs->type_id ||
call.type_id != lhs->type_id) {
break;
}
auto lhs = context.insts().GetAs<SemIR::IntLiteral>(arg_ids[0]);
auto rhs = context.insts().GetAs<SemIR::IntLiteral>(arg_ids[1]);
// TODO: Integer values should be stored in the correct bit width for
// their types. For now we assume i32.
auto lhs_val = context.ints().Get(lhs->int_id).sextOrTrunc(32);
auto rhs_val = context.ints().Get(rhs->int_id).sextOrTrunc(32);
auto lhs_val = context.ints().Get(lhs.int_id).sextOrTrunc(32);
auto rhs_val = context.ints().Get(rhs.int_id).sextOrTrunc(32);
bool overflow = false;
auto result = context.ints().Add(lhs_val.sadd_ov(rhs_val, overflow));
if (overflow) {
@@ -322,28 +315,14 @@ static auto PerformBuiltinCall(Context& context, SemIRLocation loc,
llvm::APSInt(lhs_val, false),
llvm::APSInt(rhs_val, false));
}
return MakeConstantResult(context,
SemIR::IntLiteral{lhs->type_id, result}, phase);
return MakeConstantResult(context, SemIR::IntLiteral{lhs.type_id, result},
phase);
}
}
return SemIR::ConstantId::NotConstant;
}
// Extracts the callee function from a callee constant. Returns
// FunctionId::Invalid if the callee is not known.
static auto GetCalleeFunctionId(Context& context, SemIR::InstId callee_id)
-> SemIR::FunctionId {
if (auto bound_method =
context.insts().TryGetAs<SemIR::BoundMethod>(callee_id)) {
callee_id = bound_method->function_id;
}
if (auto callee = context.insts().TryGetAs<SemIR::FunctionDecl>(callee_id)) {
return {callee->function_id};
}
return {SemIR::FunctionId::Invalid};
}
static auto PerformCall(Context& context, SemIRLocation loc, SemIR::Call call)
-> SemIR::ConstantId {
Phase phase = Phase::Template;
@@ -362,11 +341,10 @@ static auto PerformCall(Context& context, SemIRLocation loc, SemIR::Call call)
return SemIR::ConstantId::NotConstant;
}
auto function_id = GetCalleeFunctionId(context, call.callee_id);
// Handle calls to builtins.
auto& function = context.functions().Get(function_id);
if (function.builtin_kind != SemIR::BuiltinFunctionKind::None) {
if (auto builtin_function_kind = SemIR::BuiltinFunctionKind::ForCallee(
context.sem_ir(), call.callee_id);
builtin_function_kind != SemIR::BuiltinFunctionKind::None) {
if (!ReplaceFieldWithConstantValue(context, &call, &SemIR::Call::args_id,
&phase)) {
return SemIR::ConstantId::NotConstant;
@@ -374,7 +352,7 @@ static auto PerformCall(Context& context, SemIRLocation loc, SemIR::Call call)
if (phase == Phase::UnknownDueToError) {
return SemIR::ConstantId::Error;
}
return PerformBuiltinCall(context, loc, call, function.builtin_kind,
return PerformBuiltinCall(context, loc, call, builtin_function_kind,
context.inst_blocks().Get(call.args_id), phase);
}
return SemIR::ConstantId::NotConstant;