// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/sem_ir/builtin_function_kind.h" #include #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::SemIR { // A function that validates that a builtin was declared properly. using ValidateFn = auto(const File& sem_ir, llvm::ArrayRef arg_types, TypeId return_type) -> bool; namespace { // Information about a builtin function. struct BuiltinInfo { llvm::StringLiteral name; ValidateFn* validate; }; // The maximum number of type parameters any builtin needs. constexpr int MaxTypeParams = 2; // State used when validating a builtin signature that persists between // individual checks. struct ValidateState { // The type values of type parameters in the builtin signature. Invalid if // either no value has been deduced yet or the parameter is not used. TypeId type_params[MaxTypeParams] = {TypeId::Invalid, TypeId::Invalid}; }; // Constraint that a type is generic type parameter `I` of the builtin, // satisfying `TypeConstraint`. See ValidateSignature for details. template struct TypeParam { static_assert(I >= 0 && I < MaxTypeParams); static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool { if (state.type_params[I].is_valid() && type_id != state.type_params[I]) { return false; } state.type_params[I] = type_id; return TypeConstraint::Check(sem_ir, state, type_id); } }; // Constraint that a type is a specific builtin. See ValidateSignature for // details. template struct BuiltinType { static auto Check(const File& sem_ir, ValidateState& /*state*/, TypeId type_id) -> bool { return sem_ir.types().GetInstId(type_id) == BuiltinId; } }; // Constraint that the function has no return. struct NoReturn { static auto Check(const File& sem_ir, ValidateState& /*state*/, TypeId type_id) -> bool { auto tuple = sem_ir.types().TryGetAs(type_id); if (!tuple) { return false; } return sem_ir.type_blocks().Get(tuple->elements_id).empty(); } }; // Constraint that a type is `bool`. using Bool = BuiltinType; // Constraint that requires the type to be an integer type. struct AnyInt { static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool { // TODO: Support Core.BigInt once it exists. if (BuiltinType::Check(sem_ir, state, type_id)) { return true; } return sem_ir.types().Is(type_id); } }; // Constraint that requires the type to be a float type. struct AnyFloat { static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool { if (BuiltinType::Check(sem_ir, state, type_id)) { return true; } return sem_ir.types().Is(type_id); } }; // Constraint that requires the type to be the type type. using Type = BuiltinType; } // namespace // Validates that this builtin has a signature matching the specified signature. // // `SignatureFnType` is a C++ function type that describes the signature that is // expected for this builtin. For example, `auto (AnyInt, AnyInt) -> AnyInt` // specifies that the builtin takes values of two integer types and returns a // value of a third integer type. Types used within the signature should provide // a `Check` function that validates that the Carbon type is expected: // // auto Check(const File&, ValidateState&, TypeId) -> bool; // // To constrain that the same type is used in multiple places in the signature, // `TypeParam` can be used. For example: // // auto (TypeParam<0, AnyInt>, AnyInt) -> TypeParam<0, AnyInt> // // describes a builtin that takes two integers, and whose return type matches // its first parameter type. For convenience, typedefs for `TypeParam` // are used in the descriptions of the builtins. template static auto ValidateSignature(const File& sem_ir, llvm::ArrayRef arg_types, TypeId return_type) -> bool { using SignatureTraits = llvm::function_traits; ValidateState state; // Must have expected number of arguments. if (arg_types.size() != SignatureTraits::num_args) { return false; } // Argument types must match. if (![&](std::index_sequence) { return ((SignatureTraits::template arg_t::Check( sem_ir, state, arg_types[Indexes])) && ...); }(std::make_index_sequence())) { return false; } // Result type must match. if (!SignatureTraits::result_t::Check(sem_ir, state, return_type)) { return false; } return true; } // Descriptions of builtin functions follow. For each builtin, a corresponding // `BuiltinInfo` constant is declared describing properties of that builtin. namespace BuiltinFunctionInfo { // Convenience name used in the builtin type signatures below for a first // generic type parameter that is constrained to be an integer type. using IntT = TypeParam<0, AnyInt>; // Convenience name used in the builtin type signatures below for a second // generic type parameter that is constrained to be an integer type. using IntU = TypeParam<1, AnyInt>; // Convenience name used in the builtin type signatures below for a first // generic type parameter that is constrained to be an float type. using FloatT = TypeParam<0, AnyFloat>; // Not a builtin function. constexpr BuiltinInfo None = {"", nullptr}; // Prints an argument. constexpr BuiltinInfo PrintInt = {"print.int", ValidateSignatureNoReturn>}; // Returns the `i32` type. Doesn't take a bit size because we need an integer // type as a basis for that. constexpr BuiltinInfo IntMakeType32 = {"int.make_type_32", ValidateSignatureType>}; // Returns the `iN` type. // TODO: Should we use a more specific type as the type of the bit width? constexpr BuiltinInfo IntMakeTypeSigned = { "int.make_type_signed", ValidateSignatureType>}; // Returns the `uN` type. constexpr BuiltinInfo IntMakeTypeUnsigned = { "int.make_type_unsigned", ValidateSignatureType>}; // Returns float types, such as `f64`. Currently only supports `f64`. constexpr BuiltinInfo FloatMakeType = {"float.make_type", ValidateSignatureType>}; // Returns the `bool` type. constexpr BuiltinInfo BoolMakeType = {"bool.make_type", ValidateSignatureType>}; // "int.snegate": integer negation. constexpr BuiltinInfo IntSNegate = {"int.snegate", ValidateSignatureIntT>}; // "int.sadd": integer addition. constexpr BuiltinInfo IntSAdd = {"int.sadd", ValidateSignatureIntT>}; // "int.ssub": integer subtraction. constexpr BuiltinInfo IntSSub = {"int.ssub", ValidateSignatureIntT>}; // "int.smul": integer multiplication. constexpr BuiltinInfo IntSMul = {"int.smul", ValidateSignatureIntT>}; // "int.sdiv": integer division. constexpr BuiltinInfo IntSDiv = {"int.sdiv", ValidateSignatureIntT>}; // "int.smod": integer modulo. constexpr BuiltinInfo IntSMod = {"int.smod", ValidateSignatureIntT>}; // "int.unegate": unsigned integer negation. constexpr BuiltinInfo IntUNegate = {"int.unegate", ValidateSignatureIntT>}; // "int.uadd": unsigned integer addition. constexpr BuiltinInfo IntUAdd = {"int.uadd", ValidateSignatureIntT>}; // "int.usub": unsigned integer subtraction. constexpr BuiltinInfo IntUSub = {"int.usub", ValidateSignatureIntT>}; // "int.umul": unsigned integer multiplication. constexpr BuiltinInfo IntUMul = {"int.umul", ValidateSignatureIntT>}; // "int.udiv": unsigned integer division. constexpr BuiltinInfo IntUDiv = {"int.udiv", ValidateSignatureIntT>}; // "int.mod": integer modulo. constexpr BuiltinInfo IntUMod = {"int.umod", ValidateSignatureIntT>}; // "int.complement": integer bitwise complement. constexpr BuiltinInfo IntComplement = {"int.complement", ValidateSignatureIntT>}; // "int.and": integer bitwise and. constexpr BuiltinInfo IntAnd = {"int.and", ValidateSignatureIntT>}; // "int.or": integer bitwise or. constexpr BuiltinInfo IntOr = {"int.or", ValidateSignatureIntT>}; // "int.xor": integer bitwise xor. constexpr BuiltinInfo IntXor = {"int.xor", ValidateSignatureIntT>}; // "int.left_shift": integer left shift. constexpr BuiltinInfo IntLeftShift = { "int.left_shift", ValidateSignatureIntT>}; // "int.left_shift": integer right shift. constexpr BuiltinInfo IntRightShift = { "int.right_shift", ValidateSignatureIntT>}; // "int.eq": integer equality comparison. constexpr BuiltinInfo IntEq = {"int.eq", ValidateSignatureBool>}; // "int.neq": integer non-equality comparison. constexpr BuiltinInfo IntNeq = {"int.neq", ValidateSignatureBool>}; // "int.less": integer less than comparison. constexpr BuiltinInfo IntLess = {"int.less", ValidateSignatureBool>}; // "int.less_eq": integer less than or equal comparison. constexpr BuiltinInfo IntLessEq = {"int.less_eq", ValidateSignatureBool>}; // "int.greater": integer greater than comparison. constexpr BuiltinInfo IntGreater = {"int.greater", ValidateSignatureBool>}; // "int.greater_eq": integer greater than or equal comparison. constexpr BuiltinInfo IntGreaterEq = { "int.greater_eq", ValidateSignatureBool>}; // "float.negate": float negation. constexpr BuiltinInfo FloatNegate = {"float.negate", ValidateSignatureFloatT>}; // "float.add": float addition. constexpr BuiltinInfo FloatAdd = { "float.add", ValidateSignatureFloatT>}; // "float.sub": float subtraction. constexpr BuiltinInfo FloatSub = { "float.sub", ValidateSignatureFloatT>}; // "float.mul": float multiplication. constexpr BuiltinInfo FloatMul = { "float.mul", ValidateSignatureFloatT>}; // "float.div": float division. constexpr BuiltinInfo FloatDiv = { "float.div", ValidateSignatureFloatT>}; // "float.eq": float equality comparison. constexpr BuiltinInfo FloatEq = {"float.eq", ValidateSignatureBool>}; // "float.neq": float non-equality comparison. constexpr BuiltinInfo FloatNeq = { "float.neq", ValidateSignatureBool>}; // "float.less": float less than comparison. constexpr BuiltinInfo FloatLess = { "float.less", ValidateSignatureBool>}; // "float.less_eq": float less than or equal comparison. constexpr BuiltinInfo FloatLessEq = { "float.less_eq", ValidateSignatureBool>}; // "float.greater": float greater than comparison. constexpr BuiltinInfo FloatGreater = { "float.greater", ValidateSignatureBool>}; // "float.greater_eq": float greater than or equal comparison. constexpr BuiltinInfo FloatGreaterEq = { "float.greater_eq", ValidateSignatureBool>}; } // namespace BuiltinFunctionInfo CARBON_DEFINE_ENUM_CLASS_NAMES(BuiltinFunctionKind) = { #define CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(Name) \ BuiltinFunctionInfo::Name.name, #include "toolchain/sem_ir/builtin_function_kind.def" }; // Returns the builtin function kind with the given name, or None if the name // is unknown. auto BuiltinFunctionKind::ForBuiltinName(llvm::StringRef name) -> BuiltinFunctionKind { #define CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(Name) \ if (name == BuiltinFunctionInfo::Name.name) { \ return BuiltinFunctionKind::Name; \ } #include "toolchain/sem_ir/builtin_function_kind.def" return BuiltinFunctionKind::None; } auto BuiltinFunctionKind::IsValidType(const File& sem_ir, llvm::ArrayRef arg_types, TypeId return_type) const -> bool { static constexpr ValidateFn* ValidateFns[] = { #define CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(Name) \ BuiltinFunctionInfo::Name.validate, #include "toolchain/sem_ir/builtin_function_kind.def" }; return ValidateFns[AsInt()](sem_ir, arg_types, return_type); } } // namespace Carbon::SemIR