From 75713908f44a05d3508bbc1429e25caf04e7f5b3 Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Thu, 15 Jan 2026 15:15:48 -0800 Subject: [PATCH] Store and reuse lowered parameter order (#6593) This resolves a longstanding TODO in `file_context.cpp`, and prepares lowering to support compound return forms. --- toolchain/lower/file_context.cpp | 672 +++++++++++------- toolchain/lower/file_context.h | 99 ++- toolchain/lower/function_context.h | 18 - toolchain/lower/handle_call.cpp | 45 +- toolchain/lower/specific_coalescer.cpp | 9 +- toolchain/lower/specific_coalescer.h | 4 +- .../lower/testdata/builtins/no_op.carbon | 39 +- toolchain/lower/testdata/class/generic.carbon | 188 ++--- toolchain/lower/testdata/for/bindings.carbon | 138 ++-- .../function/call/empty_struct.carbon | 27 +- .../testdata/function/call/empty_tuple.carbon | 27 +- .../call/implicit_empty_tuple_as_arg.carbon | 35 +- .../testdata/function/call/ref_return.carbon | 68 ++ .../generic/type_representation.carbon | 2 +- toolchain/sem_ir/function.cpp | 19 - toolchain/sem_ir/function.h | 11 - 16 files changed, 788 insertions(+), 613 deletions(-) create mode 100644 toolchain/lower/testdata/function/call/ref_return.carbon diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 02d6c97be798..9b389b7cfa8d 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -50,7 +50,7 @@ FileContext::FileContext(Context& context, const SemIR::File& sem_ir, inst_namer_(inst_namer), vlog_stream_(vlog_stream), functions_(LoweredFunctionStore::MakeForOverwrite(sem_ir.functions())), - specific_functions_(sem_ir.specifics(), nullptr), + specific_functions_(sem_ir.specifics(), std::nullopt), types_(LoweredTypeStore::MakeWithExplicitSize( sem_ir.constant_values().ConcreteStoreSize(), sem_ir.constant_values().GetTypeIdTag(), {nullptr, nullptr})), @@ -147,12 +147,12 @@ auto FileContext::LowerDefinitions() -> void { // variables. if (auto global_ctor_id = sem_ir().global_ctor_id(); global_ctor_id.has_value()) { - auto* llvm_function = BuildFunctionDecl(global_ctor_id); + auto llvm_function = BuildFunctionDecl(global_ctor_id); functions_.Set(global_ctor_id, llvm_function); const auto& global_ctor = sem_ir().functions().Get(global_ctor_id); BuildFunctionBody(global_ctor_id, SemIR::SpecificId::None, global_ctor, *this, global_ctor); - llvm::appendToGlobalCtors(llvm_module(), llvm_function, + llvm::appendToGlobalCtors(llvm_module(), llvm_function->llvm_function, /*Priority=*/0); } } @@ -245,128 +245,359 @@ auto FileContext::GetConstant(SemIR::ConstantId const_id, return global_variable; } -auto FileContext::GetOrCreateFunction(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) - -> llvm::Function* { +auto FileContext::GetOrCreateFunctionInfo(SemIR::FunctionId function_id, + SemIR::SpecificId specific_id) + -> std::optional& { // If we have already lowered a declaration of this function, just return it. - auto** result = GetFunctionAddr(function_id, specific_id); - if (!*result) { - *result = BuildFunctionDecl(function_id, specific_id); + auto& result = GetFunctionInfo(function_id, specific_id); + if (!result) { + result = BuildFunctionDecl(function_id, specific_id); } - return *result; + return result; } -auto FileContext::BuildFunctionTypeInfo(const SemIR::Function& function, - SemIR::SpecificId specific_id) - -> FunctionTypeInfo { - const auto return_info = - SemIR::ReturnTypeInfo::ForFunction(sem_ir(), function, specific_id); +// State machine for building a FunctionTypeInfo from SemIR. +// +// The main difficulty this class encapsulates is that each abstraction level +// has different expectations about how the return is reflected in the parameter +// list. +// - In SemIR, if the function has an initializing return form, it has a +// corresponding output parameter at the end of the parameter list. +// - In LLVM IR, if the SemIR has an output parameter _and_ that parameter's +// type has an in-place initializing representation, we emit a corresponding +// `sret` output parameter (and the function's return type is void). By +// convention the output parameter goes at the start of the parameter list. +// - In LLVM debug info, the list of parameter types always starts with the +// return type (which doubles as the type of the return parameter, if there +// is one). +// +// Furthermore, SemIR is designed to eventually support compound return forms, +// in which case there can be multiple output parameters for different pieces of +// the return form, but it's not yet clear how we will lower such functions. +class FileContext::FunctionTypeInfoBuilder { + public: + // Creates a FunctionTypeInfoBuilder that uses the given FileContext, and + // the given specific of the function. + FunctionTypeInfoBuilder(FileContext* context, SemIR::SpecificId specific_id) + : context_(*context), specific_id_(specific_id) {} - if (!return_info.is_valid()) { - // The return type has not been completed, create a trivial type instead. - return {.type = - llvm::FunctionType::get(llvm::Type::getVoidTy(llvm_context()), - /*isVarArg=*/false)}; + // Retrieves various features of `function`'s type useful for constructing the + // `llvm::Type` and `llvm::DISubroutineType` for the `llvm::Function`. If any + // part of the type can't be manifest (eg: incomplete return or parameter + // types), then the result is as if the type was `void()`. Should only be + // called once on a given builder. + auto Build(const SemIR::Function& function) && -> FunctionTypeInfo; + + private: + // By convention, state transition methods return false to indicate that + // `Abort` was called. As a convenience, that applies even to methods that + // never call `Abort`, and to `Abort` itself, so that their callers can easily + // propagate the failure. + + // Resets the builder to the fallback state `void()`. This puts the builder in + // a state where Finalize can be called, and no other operation should be + // called. + auto Abort() -> bool { + lowered_param_pattern_ids_.clear(); + param_types_.clear(); + param_di_types_.clear(); + return_type_ = nullptr; + SetReturnByCopy(SemIR::TypeId::None); + return false; } - auto get_llvm_type = [&](SemIR::TypeId type_id) -> llvm::Type* { - if (!type_id.has_value()) { - return nullptr; - } - return GetType(type_id); - }; + // Handles the function's return form. The argument can be None, indicating + // that there was no explicitly declared return form. + // + // This should be called before HandleParameter. It delegates to exactly one + // of SetReturnByCopy, SetReturnByReference, SetReturnInPlace, or Abort, and + // returns false if Abort was called. + auto HandleReturnForm(SemIR::InstId return_form_inst_id) -> bool; - // TODO: expose the `Call` parameter patterns in `Function`, and use them here - // instead of reconstructing them via the syntactic parameter lists. - auto implicit_param_patterns = - sem_ir().inst_blocks().GetOrEmpty(function.implicit_param_patterns_id); - auto param_patterns = - sem_ir().inst_blocks().GetOrEmpty(function.param_patterns_id); + // Records that the LLVM function returns by copy, with type `return_type_id`. + // `return_type_id` can be `None`, which is treated as equivalent to the + // default return type `()`. + auto SetReturnByCopy(SemIR::TypeId return_type_id) -> bool { + CARBON_CHECK(return_type_ == nullptr); + CARBON_CHECK(param_di_types_.empty()); + auto lowered_return_types = GetLoweredTypes(return_type_id); + return_type_ = lowered_return_types.llvm_ir_type; + param_di_types_.push_back(lowered_return_types.llvm_di_type); + return true; + } - auto* return_type = get_llvm_type(return_info.type_id); + // Records that the LLVM function returns by reference, with type + // `return_type_id`. + auto SetReturnByReference(SemIR::TypeId /*return_type_id*/) -> bool { + return_type_ = + llvm::PointerType::get(context_.llvm_context(), /*AddressSpace=*/0); + // TODO: replace this with a reference type. + param_di_types_.push_back( + context_.context().di_builder().createPointerType(nullptr, 8)); + return true; + } - llvm::SmallVector param_types; - // Compute the return type to use for the LLVM function. If the initializing - // representation doesn't produce a value, set the return type to void. + // Records that the LLVM function returns in place, with type + // `return_type_id`. + auto SetReturnInPlace(SemIR::TypeId return_type_id) -> bool { + return_type_ = llvm::Type::getVoidTy(context_.llvm_context()); + sret_type_ = context_.GetType(return_type_id); + // We don't add to param_di_types_ because that will be handled by the + // loop over the SemIR parameters. + return true; + } + + // Handles the given `Call` parameter, which must be a *ParamPattern inst. + // This should be called on parameter patterns in the order that they should + // appear in the LLVM IR parameter list, so in particular it should be called + // on the `OutParamPattern` (if any) first. It should be called on all `Call` + // parameters; it will determine which parameters belong in the LLVM IR + // parameter list. + // + // This delegates to exactly one of AddLoweredParam, IgnoreParam, or Abort, + // and returns false if Abort was called. + auto HandleParameter(SemIR::InstId param_pattern_id) -> bool; + + // Records that the given parameter pattern is lowered to the given + // IR and DI types. + auto AddLoweredParam(SemIR::InstId param_pattern_id, LoweredTypes param_types) + -> bool { + lowered_param_pattern_ids_.push_back(param_pattern_id); + param_types_.push_back(param_types.llvm_ir_type); + param_di_types_.push_back(param_types.llvm_di_type); + return true; + } + + // Records that the given parameter pattern is not lowered to an LLVM + // parameter. + auto IgnoreParam(SemIR::InstId param_pattern_id) -> bool { + unused_param_pattern_ids_.push_back(param_pattern_id); + return true; + } + + // Builds and returns a FunctionTypeInfo from the accumulated information. + auto Finalize() -> FunctionTypeInfo; + + // Returns LLVM IR and DI types for the given SemIR type. This is not a state + // transition. It mostly delegates to context_.GetTypeAndDIType, but treats + // TypeId::None as equivalent to the unit type, and uses an untyped pointer as + // a placeholder DI type if context_ doesn't provide one. + auto GetLoweredTypes(SemIR::TypeId type_id) -> LoweredTypes; + + FileContext& context_; + const SemIR::SpecificId specific_id_; + + // The types of the parameters in the LLVM IR function. Each one corresponds + // to a SemIR `Call` parameter, but some `Call` parameters may be omitted + // (e.g. if they are stateless) or reordered (e.g. the return parameter, if + // any, always goes first). + llvm::SmallVector param_types_; + + // The LLLVM DI representation of the parameter list. As required by LLVM DI + // convention, this starts with the function's return type, and ends with the + // DI representations of param_types_ (in the same order). Note that those + // two ranges may overlap: if the first element of param_types_ represents + // a return parameter, the first element of param_di_types_ corresponds to it + // while also representing the return type. + llvm::SmallVector param_di_types_; + + // The SemIR function's `Call` param patterns that correspond to param_types_, + // in the same order. + llvm::SmallVector lowered_param_pattern_ids_; + + // Any `Call` param patterns that aren't present in + // reordered_param_pattern_ids_. + llvm::SmallVector unused_param_pattern_ids_; + + // The `index` member of the SemIR function's return parameter, or -1 if it + // has no return parameter. Note that even if the SemIR function has a return + // parameter, the LLVM IR function might not. + int semir_return_param_index_ = -1; + + // The LLVM function's return type. + llvm::Type* return_type_ = nullptr; + + // If not null, the LLVM function's first parameter should have a `sret` + // attribute with this type. + llvm::Type* sret_type_ = nullptr; +}; + +auto FileContext::FunctionTypeInfoBuilder::Build( + const SemIR::Function& function) && -> FunctionTypeInfo { // TODO: For the `Run` entry point, remap return type to i32 if it doesn't // return a value. - llvm::Type* function_return_type = - (return_info.is_valid() && - return_info.init_repr.kind == SemIR::InitRepr::ByCopy) - ? return_type - : llvm::Type::getVoidTy(llvm_context()); - // TODO: Consider either storing `param_inst_ids` somewhere so that we can - // reuse it from `BuildFunctionDefinition` and when building calls, or factor - // out a mechanism to compute the mapping between parameters and arguments on - // demand. - llvm::SmallVector param_inst_ids; - auto max_llvm_params = (return_info.has_return_slot() ? 1 : 0) + - implicit_param_patterns.size() + param_patterns.size(); - param_types.reserve(max_llvm_params); - param_inst_ids.reserve(max_llvm_params); - auto return_param_id = SemIR::InstId::None; - if (return_info.has_return_slot()) { - param_types.push_back( - llvm::PointerType::get(llvm_context(), /*AddressSpace=*/0)); - auto return_patterns = - sem_ir_->inst_blocks().Get(function.return_patterns_id); - CARBON_CHECK(return_patterns.size() == 1, - "TODO: implement support for multiple return params"); - return_param_id = return_patterns[0]; - param_inst_ids.push_back(return_param_id); + auto call_param_pattern_ids = + context_.sem_ir().inst_blocks().Get(function.call_param_patterns_id); + lowered_param_pattern_ids_.reserve(call_param_pattern_ids.size()); + param_types_.reserve(call_param_pattern_ids.size()); + param_di_types_.reserve(call_param_pattern_ids.size()); + + if (!HandleReturnForm(function.return_form_inst_id)) { + return Finalize(); } - for (auto param_pattern_id : llvm::concat( - implicit_param_patterns, param_patterns)) { - // TODO: Handle a general pattern here, rather than assuming that each - // parameter pattern contains at most one binding. - auto param_pattern_info = SemIR::Function::GetParamPatternInfoFromPatternId( - sem_ir(), param_pattern_id); - if (!param_pattern_info) { - continue; - } - // TODO: Use a more general mechanism to determine if the binding is a - // reference binding. - if (param_pattern_info->inst.kind == SemIR::RefParamPattern::Kind || - param_pattern_info->inst.kind == SemIR::VarParamPattern::Kind) { - param_types.push_back( - llvm::PointerType::get(llvm_context(), /*AddressSpace=*/0)); - param_inst_ids.push_back(param_pattern_id); - continue; - } - auto param_type_id = ExtractScrutineeType( - sem_ir(), SemIR::GetTypeOfInstInSpecific(sem_ir(), specific_id, - param_pattern_info->inst_id)); - CARBON_CHECK( - !param_type_id.AsConstantId().is_symbolic(), - "Found symbolic type id after resolution when lowering type {0}.", - param_pattern_info->inst.type_id); - switch (auto value_rep = SemIR::ValueRepr::ForType(sem_ir(), param_type_id); - value_rep.kind) { - case SemIR::ValueRepr::Unknown: - // This parameter type is incomplete. Fallback to describing the - // function type as `void()`. - return {.type = llvm::FunctionType::get( - llvm::Type::getVoidTy(llvm_context()), - /*isVarArg=*/false)}; - case SemIR::ValueRepr::Dependent: - CARBON_FATAL("Lowering function with dependent parameter type"); - case SemIR::ValueRepr::None: - break; - case SemIR::ValueRepr::Copy: - case SemIR::ValueRepr::Custom: - case SemIR::ValueRepr::Pointer: - auto* param_types_to_add = get_llvm_type(value_rep.type_id); - param_types.push_back(param_types_to_add); - param_inst_ids.push_back(param_pattern_id); - break; + if (semir_return_param_index_ >= 0) { + CARBON_CHECK(semir_return_param_index_ == + static_cast(call_param_pattern_ids.size()) - 1, + "Unexpected parameter order"); + // Handle the return parameter first, because it goes first in the LLVM + // convention. We remove it from call_param_pattern_ids so we don't revisit + // it in the subsequent loop. + if (!HandleParameter(call_param_pattern_ids.consume_back())) { + return Finalize(); } } - return {.type = llvm::FunctionType::get(function_return_type, param_types, + for (auto param_pattern_id : call_param_pattern_ids) { + if (!HandleParameter(param_pattern_id)) { + return Finalize(); + } + } + + return Finalize(); +} + +auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm( + SemIR::InstId return_form_inst_id) -> bool { + if (!return_form_inst_id.has_value()) { + return SetReturnByCopy(SemIR::TypeId::None); + } + + auto return_form_const_id = SemIR::GetConstantValueInSpecific( + context_.sem_ir(), specific_id_, return_form_inst_id); + auto return_form_inst = context_.sem_ir().insts().Get( + context_.sem_ir().constant_values().GetInstId(return_form_const_id)); + CARBON_KIND_SWITCH(return_form_inst) { + case CARBON_KIND(SemIR::InitForm init_form): { + CARBON_CHECK( + std::exchange(semir_return_param_index_, init_form.index.index) == -1, + "TODO: Generalize this to support compound return forms"); + auto return_type_id = + context_.sem_ir().types().GetTypeIdForTypeConstantId( + SemIR::GetConstantValueInSpecific( + context_.sem_ir(), specific_id_, + init_form.type_component_inst_id)); + switch ( + SemIR::InitRepr::ForType(context_.sem_ir(), return_type_id).kind) { + case SemIR::InitRepr::InPlace: { + return SetReturnInPlace(return_type_id); + } + case SemIR::InitRepr::ByCopy: { + return SetReturnByCopy(return_type_id); + } + case SemIR::InitRepr::None: + return SetReturnByCopy(SemIR::TypeId::None); + case SemIR::InitRepr::Dependent: + case SemIR::InitRepr::Incomplete: + case SemIR::InitRepr::Abstract: + return Abort(); + } + } + case CARBON_KIND(SemIR::RefForm ref_form): { + auto return_type_id = + context_.sem_ir().types().GetTypeIdForTypeConstantId( + SemIR::GetConstantValueInSpecific( + context_.sem_ir(), specific_id_, + ref_form.type_component_inst_id)); + return SetReturnByReference(return_type_id); + } + default: + CARBON_FATAL("Unexpected inst kind: {0}", return_form_inst); + } +} + +auto FileContext::FunctionTypeInfoBuilder::HandleParameter( + SemIR::InstId param_pattern_id) -> bool { + auto param_pattern = context_.sem_ir().insts().Get(param_pattern_id); + auto param_type_id = ExtractScrutineeType( + context_.sem_ir(), + SemIR::GetTypeOfInstInSpecific(context_.sem_ir(), specific_id_, + param_pattern_id)); + + // Returns the appropriate LoweredTypes for reference-like parameters. + auto ref_lowered_types = [&]() -> LoweredTypes { + return {.llvm_ir_type = llvm::PointerType::get(context_.llvm_context(), + /*AddressSpace=*/0), + // TODO: replace this with a reference type. + .llvm_di_type = GetLoweredTypes(param_type_id).llvm_di_type}; + }; + + CARBON_CHECK( + !param_type_id.AsConstantId().is_symbolic(), + "Found symbolic type id after resolution when lowering type {0}.", + param_pattern.type_id()); + CARBON_KIND_SWITCH(param_pattern) { + case SemIR::RefParamPattern::Kind: + case SemIR::VarParamPattern::Kind: { + return AddLoweredParam(param_pattern_id, ref_lowered_types()); + } + case SemIR::OutParamPattern::Kind: { + switch (SemIR::InitRepr::ForType(context_.sem_ir(), param_type_id).kind) { + case SemIR::InitRepr::InPlace: + return AddLoweredParam(param_pattern_id, ref_lowered_types()); + case SemIR::InitRepr::ByCopy: + case SemIR::InitRepr::None: + return IgnoreParam(param_pattern_id); + case SemIR::InitRepr::Dependent: + case SemIR::InitRepr::Incomplete: + case SemIR::InitRepr::Abstract: + return Abort(); + } + } + case SemIR::ValueParamPattern::Kind: { + switch (auto value_rep = + SemIR::ValueRepr::ForType(context_.sem_ir(), param_type_id); + value_rep.kind) { + case SemIR::ValueRepr::Unknown: + return Abort(); + case SemIR::ValueRepr::Dependent: + CARBON_FATAL("Lowering function parameter with dependent type: {0}", + param_pattern); + case SemIR::ValueRepr::None: + return IgnoreParam(param_pattern_id); + case SemIR::ValueRepr::Copy: + case SemIR::ValueRepr::Custom: + case SemIR::ValueRepr::Pointer: { + if (value_rep.type_id.has_value()) { + return AddLoweredParam(param_pattern_id, + GetLoweredTypes(value_rep.type_id)); + } else { + return IgnoreParam(param_pattern_id); + } + } + } + } + default: + CARBON_FATAL("Unexpected inst kind: {0}", param_pattern); + } +} + +auto FileContext::FunctionTypeInfoBuilder::Finalize() -> FunctionTypeInfo { + CARBON_CHECK(!param_di_types_.empty()); + auto& di_builder = context_.context().di_builder(); + return {.type = llvm::FunctionType::get(return_type_, param_types_, /*isVarArg=*/false), - .param_inst_ids = std::move(param_inst_ids), - .return_type = return_type, - .return_param_id = return_param_id}; + .di_type = di_builder.createSubroutineType( + di_builder.getOrCreateTypeArray(param_di_types_), + llvm::DINode::FlagZero), + .lowered_param_pattern_ids = std::move(lowered_param_pattern_ids_), + .unused_param_pattern_ids = std::move(unused_param_pattern_ids_), + .sret_type = sret_type_}; +} + +auto FileContext::FunctionTypeInfoBuilder::GetLoweredTypes( + SemIR::TypeId type_id) -> LoweredTypes { + if (!type_id.has_value()) { + return {.llvm_ir_type = llvm::Type::getVoidTy(context_.llvm_context()), + .llvm_di_type = nullptr}; + } + auto result = context_.GetTypeAndDIType(type_id); + if (result.llvm_di_type == nullptr) { + // TODO: figure out what type should go here, or ensure this doesn't + // happen. + result.llvm_di_type = + context_.context().di_builder().createPointerType(nullptr, 8); + } + return result; } auto FileContext::HandleReferencedCppFunction(clang::FunctionDecl* cpp_decl) @@ -408,18 +639,18 @@ auto FileContext::HandleReferencedSpecificFunction( auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, SemIR::SpecificId specific_id) - -> llvm::Function* { + -> std::optional { const auto& function = sem_ir().functions().Get(function_id); // Don't lower generic functions. Note that associated functions in interfaces // have `Self` in scope, so are implicitly generic functions. if (function.generic_id.has_value() && !specific_id.has_value()) { - return nullptr; + return std::nullopt; } // Don't lower builtins. if (function.builtin_function_kind() != SemIR::BuiltinFunctionKind::None) { - return nullptr; + return std::nullopt; } // Don't lower C++ functions that use a thunk. We will never reference them @@ -427,13 +658,14 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, // corresponding C++ function anyway. if (function.special_function_kind == SemIR::Function::SpecialFunctionKind::HasCppThunk) { - return nullptr; + return std::nullopt; } // TODO: Consider tracking whether the function has been used, and only // lowering it if it's needed. - auto function_type_info = BuildFunctionTypeInfo(function, specific_id); + auto function_type_info = + FunctionTypeInfoBuilder(this, specific_id).Build(function); // TODO: For an imported inline function, consider generating an // `available_externally` definition. @@ -456,7 +688,13 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, // diagnosed by check if detected, but it's not clear that check will always // be able to see this problem. In theory, name collisions could also occur // due to fingerprint collision. - return existing; + return {{.type = function_type_info.type, + .di_type = function_type_info.di_type, + .lowered_param_pattern_ids = + std::move(function_type_info.lowered_param_pattern_ids), + .unused_param_pattern_ids = + std::move(function_type_info.unused_param_pattern_ids), + .llvm_function = existing}}; } // If this is a C++ function, tell Clang that we referenced it. @@ -484,20 +722,25 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, "Mangled name collision: {0}", mangled_name); // Set up parameters and the return slot. - for (auto [inst_id, arg] : llvm::zip_equal(function_type_info.param_inst_ids, - llvm_function->args())) { - auto name_id = SemIR::NameId::None; - if (inst_id == function_type_info.return_param_id) { - name_id = SemIR::NameId::ReturnSlot; - arg.addAttr(llvm::Attribute::getWithStructRetType( - llvm_context(), function_type_info.return_type)); - } else { - name_id = SemIR::GetPrettyNameFromPatternId(sem_ir(), inst_id); - } - arg.setName(sem_ir().names().GetIRBaseName(name_id)); + for (auto [inst_id, arg] : + llvm::zip_equal(function_type_info.lowered_param_pattern_ids, + llvm_function->args())) { + arg.setName(sem_ir().names().GetIRBaseName( + SemIR::GetPrettyNameFromPatternId(sem_ir(), inst_id))); + } + if (function_type_info.sret_type != nullptr) { + auto& return_arg = *llvm_function->args().begin(); + return_arg.addAttr(llvm::Attribute::getWithStructRetType( + llvm_context(), function_type_info.sret_type)); } - return llvm_function; + return {{.type = function_type_info.type, + .di_type = function_type_info.di_type, + .lowered_param_pattern_ids = + std::move(function_type_info.lowered_param_pattern_ids), + .unused_param_pattern_ids = + std::move(function_type_info.unused_param_pattern_ids), + .llvm_function = llvm_function}}; } // Find the file and function ID describing the definition of a function. @@ -576,8 +819,8 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, // instead of our context. const auto& definition_ir = definition_context.sem_ir(); - auto* llvm_function = GetFunction(function_id, specific_id); - CARBON_CHECK(llvm_function, + auto function_info = GetFunctionInfo(function_id, specific_id); + CARBON_CHECK(function_info && function_info->llvm_function, "Attempting to define function that was not declared"); const auto& body_block_ids = definition_function.body_block_ids; @@ -619,78 +862,45 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, // TODO: Should we generate an InlineHint for some functions? Perhaps for // those defined in the API file? } - llvm_function->addFnAttrs(attr_builder); + function_info->llvm_function->addFnAttrs(attr_builder); } - auto* subprogram = - BuildDISubprogram(declaration_function, specific_id, llvm_function); + auto* subprogram = BuildDISubprogram(declaration_function, *function_info); FunctionContext function_lowering( - definition_context, llvm_function, *this, specific_id, + definition_context, function_info->llvm_function, *this, specific_id, coalescer_.InitializeFingerprintForSpecific(specific_id), subprogram, vlog_stream_); - // Add parameters to locals. - // TODO: This duplicates the mapping between sem_ir instructions and LLVM - // function parameters that was already computed in BuildFunctionDecl. - // We should only do that once. auto call_param_ids = definition_ir.inst_blocks().GetOrEmpty( definition_function.call_params_id); - int param_index = 0; - // TODO: Find a way to ensure this code and the function-call lowering use - // the same parameter ordering. - - // Lowers the given parameter. Must be called in LLVM calling convention - // parameter order. - auto lower_param = [&](SemIR::InstId param_id) { - // Get the value of the parameter from the function argument. - llvm::Value* param_value; - - // The `type_id` of a parameter tracks the parameter's type. - CARBON_CHECK(definition_ir.insts().Is(param_id)); - auto param_type = function_lowering.GetTypeIdOfInst(param_id); - if (function_lowering.GetValueRepr(param_type).repr.kind != - SemIR::ValueRepr::None) { - param_value = llvm_function->getArg(param_index); - ++param_index; - } else { - param_value = - llvm::PoisonValue::get(function_lowering.GetType(param_type)); - } - // The value of the parameter is the value of the argument. - function_lowering.SetLocal(param_id, param_value); + // Returns the AnyParam inst with the same index as param_pattern_id + // (which must be an AnyParamPattern). + auto param_for_param_pattern = + [&](SemIR::InstId param_pattern_id) -> SemIR::InstId { + auto sem_ir_index = sem_ir() + .insts() + .GetAs(param_pattern_id) + .index.index; + return call_param_ids[sem_ir_index]; }; - // Lower to the return slot parameter. - auto return_patterns = sem_ir_->inst_blocks().GetOrEmpty( - declaration_function.return_patterns_id); - if (!return_patterns.empty()) { - CARBON_CHECK(sem_ir_->inst_blocks() - .Get(declaration_function.return_patterns_id) - .size() == 1, - "TODO: implement support for multiple return patterns"); - auto call_param_id = call_param_ids.consume_back(); - // The LLVM calling convention has the return slot first rather than last. - // Note that this queries whether there is a return slot at the LLVM level, - // whereas `return_patterns.empty()` queries whether there are any output - // parameters at the SemIR level. - if (SemIR::ReturnTypeInfo::ForFunction(sem_ir(), declaration_function, - specific_id) - .has_return_slot()) { - lower_param(call_param_id); - } else { - // The return slot might still be mentioned as a destination location, but - // shouldn't actually be used for anything, so we can use a poison value - // for it. - function_lowering.SetLocal(call_param_id, - llvm::PoisonValue::get(llvm::PointerType::get( - llvm_context(), /*AddressSpace=*/0))); - } + // Add local variables for the parameters. + for (auto [llvm_index, param_pattern_id] : + llvm::enumerate(function_info->lowered_param_pattern_ids)) { + function_lowering.SetLocal( + param_for_param_pattern(param_pattern_id), + function_info->llvm_function->getArg(llvm_index)); } - // Lower the remaining call parameters. - for (auto param_id : call_param_ids) { - lower_param(param_id); + // Add local variables for the SemIR parameters that aren't LLVM parameters. + // These shouldn't actually be used, so they're set to poison values. + for (auto [llvm_index, param_pattern_id] : + llvm::enumerate(function_info->unused_param_pattern_ids)) { + auto param_id = param_for_param_pattern(param_pattern_id); + function_lowering.SetLocal( + param_id, + llvm::PoisonValue::get(function_lowering.GetTypeOfInst(param_id))); } auto decl_block_id = SemIR::InstBlockId::None; @@ -709,7 +919,7 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, CARBON_VLOG("Lowering {0}\n", block_id); auto* llvm_block = function_lowering.GetBlock(block_id); // Keep the LLVM blocks in lexical order. - llvm_block->moveBefore(llvm_function->end()); + llvm_block->moveBefore(function_info->llvm_function->end()); function_lowering.builder().SetInsertPoint(llvm_block); function_lowering.LowerBlockContents(block_id); }; @@ -734,10 +944,10 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, } // LLVM requires that the entry block has no predecessors. - auto* entry_block = &llvm_function->getEntryBlock(); + auto* entry_block = &function_info->llvm_function->getEntryBlock(); if (entry_block->hasNPredecessorsOrMore(1)) { auto* new_entry_block = llvm::BasicBlock::Create( - llvm_context(), "entry", llvm_function, entry_block); + llvm_context(), "entry", function_info->llvm_function, entry_block); llvm::BranchInst::Create(entry_block, new_entry_block); } @@ -746,77 +956,8 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, context().di_builder().finalizeSubprogram(subprogram); } -auto FileContext::BuildDISubroutineType(const SemIR::Function& function, - SemIR::SpecificId specific_id) - -> llvm::DISubroutineType* { - auto implicit_param_patterns = - sem_ir().inst_blocks().GetOrEmpty(function.implicit_param_patterns_id); - auto param_patterns = - sem_ir().inst_blocks().GetOrEmpty(function.param_patterns_id); - - auto* void_pointer_debug_type = - context().di_builder().createPointerType(nullptr, 8); - - auto get_debug_type = [&](SemIR::TypeId type_id) -> llvm::DIType* { - CARBON_CHECK(type_id.has_value()); - if (auto* type = GetTypeAndDIType(type_id).llvm_di_type) { - return type; - } - return void_pointer_debug_type; - }; - - auto return_info = - SemIR::ReturnTypeInfo::ForFunction(sem_ir(), function, specific_id); - if (function.return_type_inst_id.has_value()) { - // TODO: If int_repr.kind == SemIR::InitRepr::ByCopy - be sure the return - // type is tagged with indirect calling convention. - } - - // TODO: Expose the `Call` parameter patterns in `Function`, and use them - // here. - llvm::SmallVector element_types; - element_types.push_back(return_info.type_id.has_value() - ? get_debug_type(return_info.type_id) - : nullptr); - - for (auto param_pattern_id : llvm::concat( - implicit_param_patterns, param_patterns)) { - auto param_pattern_info = SemIR::Function::GetParamPatternInfoFromPatternId( - sem_ir(), param_pattern_id); - if (!param_pattern_info) { - continue; - } - if (param_pattern_info->inst.kind == SemIR::RefParamPattern::Kind) { - // TODO: Maybe make the parameter type a reference type. - } - auto param_type_id = ExtractScrutineeType( - sem_ir(), SemIR::GetTypeOfInstInSpecific(sem_ir(), specific_id, - param_pattern_info->inst_id)); - switch (auto value_rep = SemIR::ValueRepr::ForType(sem_ir(), param_type_id); - value_rep.kind) { - case SemIR::ValueRepr::Unknown: - CARBON_FATAL("Lowering function with incomplete parameter type"); - case SemIR::ValueRepr::Dependent: - CARBON_FATAL("Lowering function with dependent parameter type"); - case SemIR::ValueRepr::None: - break; - case SemIR::ValueRepr::Copy: - case SemIR::ValueRepr::Custom: - case SemIR::ValueRepr::Pointer: - auto* param_type = get_debug_type(value_rep.type_id); - element_types.push_back(param_type); - break; - } - } - - return context().di_builder().createSubroutineType( - context().di_builder().getOrCreateTypeArray(element_types), - llvm::DINode::FlagZero); -} - auto FileContext::BuildDISubprogram(const SemIR::Function& function, - SemIR::SpecificId specific_id, - const llvm::Function* llvm_function) + const FunctionInfo& function_info) -> llvm::DISubprogram* { if (!context().di_compile_unit()) { return nullptr; @@ -825,16 +966,18 @@ auto FileContext::BuildDISubprogram(const SemIR::Function& function, CARBON_CHECK(name, "Unexpected special name for function: {0}", function.name_id); auto loc = GetLocForDI(function.definition_id); - llvm::DISubroutineType* subroutine_type = - BuildDISubroutineType(function, specific_id); + llvm::DISubroutineType* subroutine_type = function_info.di_type; auto* subprogram = context().di_builder().createFunction( - context().di_compile_unit(), *name, llvm_function->getName(), + context().di_compile_unit(), *name, + function_info.llvm_function->getName(), /*File=*/context().di_builder().createFile(loc.filename, ""), /*LineNo=*/loc.line_number, subroutine_type, /*ScopeLine=*/0, llvm::DINode::FlagZero, llvm::DISubprogram::SPFlagDefinition); // Add a variable for each parameter, as that is where DWARF debug information // comes from. + // TODO: this doesn't declare a variable for the output parameter. Is that + // what we want? for (auto [argument_number, type] : llvm::enumerate(llvm::drop_begin(subroutine_type->getTypeArray()))) { context().di_builder().createParameterVariable( @@ -1106,7 +1249,8 @@ auto FileContext::BuildVtable(const SemIR::Vtable& vtable, vfuncs.push_back(llvm::ConstantExpr::getTrunc( llvm::ConstantExpr::getSub( llvm::ConstantExpr::getPtrToInt( - GetOrCreateFunction(fn_id, fn_specific_id), i64_type), + GetOrCreateFunctionInfo(fn_id, fn_specific_id)->llvm_function, + i64_type), vtable_const_int), i32_type)); } diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index 2d871d398406..b0f8618bdf45 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -16,6 +16,29 @@ namespace Carbon::Lower { +// Information about how a given function declaration is lowered. +struct FunctionInfo { + // The type of the lowered function. + llvm::FunctionType* type; + + // The debug info type of the lowered function. + llvm::DISubroutineType* di_type; + + // The `Call` parameter patterns that correspond to parameters of the LLVM IR + // function, in the order of the LLVM IR parameter list. Some `Call` + // parameters may be omitted (e.g. if they are stateless), and the order may + // differ from the SemIR `Call` parameter list (e.g. the return parameter, if + // any, always goes first). + llvm::SmallVector lowered_param_pattern_ids; + + // Any `Call` param patterns that aren't present in + // lowered_param_pattern_ids. + llvm::SmallVector unused_param_pattern_ids; + + // The lowered function declaration. + llvm::Function* llvm_function; +}; + // Context and shared functionality for lowering within a SemIR file. class FileContext { public: @@ -43,12 +66,25 @@ class FileContext { auto GetFunction(SemIR::FunctionId function_id, SemIR::SpecificId specific_id = SemIR::SpecificId::None) -> llvm::Function* { - return *GetFunctionAddr(function_id, specific_id); + const auto& function_info = GetFunctionInfo(function_id, specific_id); + return function_info ? function_info->llvm_function : nullptr; } - // Gets a or creates callable's function. Returns nullptr for a builtin. - auto GetOrCreateFunction(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) -> llvm::Function*; + // Returns the FunctionInfo for the given function in the given specific, if + // it has already been computed. + auto GetFunctionInfo(SemIR::FunctionId function_id, + SemIR::SpecificId specific_id) + -> std::optional& { + return specific_id.has_value() ? specific_functions_.Get(specific_id) + : functions_.Get(function_id); + } + + // Returns the FunctionInfo for the given function in the given specific. If + // it's not already available, this function will compute it, including + // creating the `llvm::Function` for it. Returns nullopt for a builtin. + auto GetOrCreateFunctionInfo(SemIR::FunctionId function_id, + SemIR::SpecificId specific_id) + -> std::optional&; // Returns a lowered type for the given type_id. auto GetType(SemIR::TypeId type_id) -> llvm::Type* { @@ -128,20 +164,6 @@ class FileContext { context().SetPrintfIntFormatString(printf_int_format_string); } - struct FunctionTypeInfo { - llvm::FunctionType* type; - llvm::SmallVector param_inst_ids; - llvm::Type* return_type = nullptr; - SemIR::InstId return_param_id = SemIR::InstId::None; - }; - - // Retrieve various features of the function's type useful for constructing - // the `llvm::Type` for the `llvm::Function`. If any part of the type can't be - // manifest (eg: incomplete return or parameter types), then the result is as - // if the type was `void()`. - auto BuildFunctionTypeInfo(const SemIR::Function& function, - SemIR::SpecificId specific_id) -> FunctionTypeInfo; - // Builds the global for the given instruction, which should then be cached by // the caller. auto BuildGlobalVariableDecl(SemIR::VarStorage var_storage) @@ -156,13 +178,6 @@ class FileContext { SemIR::SpecificId specific_id = SemIR::SpecificId::None) -> void; private: - // Gets the location in which a callable's function is stored. - auto GetFunctionAddr(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) -> llvm::Function** { - return specific_id.has_value() ? &specific_functions_.Get(specific_id) - : &functions_.Get(function_id); - } - // Notes that a C++ function has been referenced for the first time, so we // should ask Clang to generate a definition for it if possible. auto HandleReferencedCppFunction(clang::FunctionDecl* cpp_decl) -> void; @@ -175,11 +190,25 @@ class FileContext { SemIR::SpecificId specific_id, llvm::Type* llvm_type) -> void; + struct FunctionTypeInfo { + llvm::FunctionType* type; + llvm::DISubroutineType* di_type; + llvm::SmallVector lowered_param_pattern_ids; + llvm::SmallVector unused_param_pattern_ids; + + // When return_param_id is not `None`, the corresponding lowered parameter + // should be given an `sret` attribute with this type. + llvm::Type* sret_type = nullptr; + }; + + class FunctionTypeInfoBuilder; + // Builds the declaration for the given function, which should then be cached // by the caller. - auto BuildFunctionDecl(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id = - SemIR::SpecificId::None) -> llvm::Function*; + auto BuildFunctionDecl( + SemIR::FunctionId function_id, + SemIR::SpecificId specific_id = SemIR::SpecificId::None) + -> std::optional; // Builds a function's body. Common functionality for all functions. // @@ -196,16 +225,9 @@ class FileContext { // Build the DISubprogram metadata for the given function. auto BuildDISubprogram(const SemIR::Function& function, - SemIR::SpecificId specific_id, - const llvm::Function* llvm_function) + const FunctionInfo& function_info) -> llvm::DISubprogram*; - // Build a `DISubroutineType` for the given function, including the return and - // parameter types. - auto BuildDISubroutineType(const SemIR::Function&, - SemIR::SpecificId specific_id) - -> llvm::DISubroutineType*; - // Builds the `llvm::Type` and `llvm::DIType` for the given instruction, which // should then be cached by the caller. auto BuildType(SemIR::InstId inst_id) -> LoweredTypes; @@ -239,12 +261,13 @@ class FileContext { // Maps callables to lowered functions. SemIR treats callables as the // canonical form of a function, so lowering needs to do the same. using LoweredFunctionStore = - FixedSizeValueStore, Tag>; LoweredFunctionStore functions_; // Maps specific callables to lowered functions. - FixedSizeValueStore> + FixedSizeValueStore, + Tag> specific_functions_; // Provides lowered versions of types. Entries are non-symbolic types. diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index 6b2d080544fb..e9784c8e8334 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -129,24 +129,6 @@ class FunctionContext { sem_ir().insts().Get(inst_id)); } - // Gets a callable's function. - auto GetFunction(SemIR::FunctionId function_id) -> llvm::Function* { - return file_context_->GetFunction(function_id); - } - - // Gets or creates a callable's function. - auto GetOrCreateFunction(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) -> llvm::Function* { - return file_context_->GetOrCreateFunction(function_id, specific_id); - } - - // Builds LLVM function type information for the specified function. - auto BuildFunctionTypeInfo(const SemIR::Function& function, - SemIR::SpecificId specific_id) - -> FileContext::FunctionTypeInfo { - return file_context_->BuildFunctionTypeInfo(function, specific_id); - } - // Returns a lowered type for the given type_id in the given file. This adds // the specified type to the fingerprint. auto GetType(TypeInFile type) -> llvm::Type* { diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index a1c5babd8fe1..146a29a60ea2 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -535,10 +535,10 @@ static auto HandleVirtualCall(FunctionContext& context, auto* i32_type = llvm::IntegerType::getInt32Ty(context.llvm_context()); auto* pointer_type = llvm::PointerType::get(context.llvm_context(), /* address space */ 0); - auto function_type_info = + auto function_info = context.GetFileContext(callee_file) - .BuildFunctionTypeInfo(function, - callee_function.resolved_specific_id); + .GetOrCreateFunctionInfo(callee_function.function_id, + callee_function.resolved_specific_id); llvm::Value* virtual_fn; if (function.clang_decl_id.has_value()) { // Use absolute vtables for clang interop - the itanium vtable contains @@ -566,9 +566,9 @@ static auto HandleVirtualCall(FunctionContext& context, llvm::ConstantInt::get( i32_type, static_cast(function.virtual_index) * 4)}); } - return context.builder().CreateCall(function_type_info.type, virtual_fn, - args); + return context.builder().CreateCall(function_info->type, virtual_fn, args); } + auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::Call inst) -> void { llvm::ArrayRef arg_ids = @@ -612,33 +612,24 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, return; } + auto& function_info = + context.GetFileContext(callee.file) + .GetOrCreateFunctionInfo(callee_function.function_id, + callee_function.resolved_specific_id); + + // Lower args in the LLVM parameter order, rather than the SemIR parameter + // order. std::vector args; - - bool call_has_return_slot = - SemIR::ReturnTypeInfo::ForCallee(context.sem_ir(), inst.callee_id) - .has_return_slot(); - if (context.GetReturnTypeInfo(callee).info.has_return_slot()) { - CARBON_CHECK(call_has_return_slot); - args.push_back(context.GetValue(arg_ids.consume_back())); - } else if (call_has_return_slot) { - // Call instruction has a return slot but this specific callee does not. - // Just ignore it. - arg_ids.consume_back(); - } - - for (auto arg_id : arg_ids) { - auto arg_type = context.GetTypeIdOfInst(arg_id); - if (context.GetValueRepr(arg_type).repr.kind != SemIR::ValueRepr::None) { - args.push_back(context.GetValue(arg_id)); - } + for (auto param_pattern_id : function_info->lowered_param_pattern_ids) { + auto sem_ir_index = callee.file->insts() + .GetAs(param_pattern_id) + .index.index; + args.push_back(context.GetValue(arg_ids[sem_ir_index])); } llvm::CallInst* call; if (function.virtual_modifier == SemIR::Function::VirtualModifier::None) { - auto* llvm_callee = - context.GetFileContext(callee.file) - .GetOrCreateFunction(callee_function.function_id, - callee_function.resolved_specific_id); + auto* llvm_callee = function_info->llvm_function; auto describe_call = [&] { RawStringOstream out; out << "call "; diff --git a/toolchain/lower/specific_coalescer.cpp b/toolchain/lower/specific_coalescer.cpp index 24a15f9aaf08..553a86313595 100644 --- a/toolchain/lower/specific_coalescer.cpp +++ b/toolchain/lower/specific_coalescer.cpp @@ -6,6 +6,7 @@ #include "common/check.h" #include "common/vlog.h" +#include "toolchain/lower/file_context.h" namespace Carbon::Lower { @@ -172,11 +173,11 @@ auto SpecificCoalescer::UpdateAndDeleteLLVMFunction( LoweredLlvmFunctionStore& lowered_llvm_functions, SemIR::SpecificId specific_id) -> void { UpdateEquivalentSpecific(specific_id); - auto* old_function = lowered_llvm_functions.Get(specific_id); - auto* new_function = + auto& old_function = lowered_llvm_functions.Get(specific_id); + auto& new_function = lowered_llvm_functions.Get(equivalent_specifics_.Get(specific_id)); - old_function->replaceAllUsesWith(new_function); - old_function->eraseFromParent(); + old_function->llvm_function->replaceAllUsesWith(new_function->llvm_function); + old_function->llvm_function->eraseFromParent(); lowered_llvm_functions.Set(specific_id, new_function); } diff --git a/toolchain/lower/specific_coalescer.h b/toolchain/lower/specific_coalescer.h index 74bf4b2c39ea..7f627b43b04a 100644 --- a/toolchain/lower/specific_coalescer.h +++ b/toolchain/lower/specific_coalescer.h @@ -11,6 +11,8 @@ namespace Carbon::Lower { +struct FunctionInfo; + // Coalescing functionality for lowering fewer specifics of the same generic. class SpecificCoalescer { public: @@ -19,7 +21,7 @@ class SpecificCoalescer { llvm::SmallVector, Tag>; using LoweredLlvmFunctionStore = - FixedSizeValueStore, Tag>; // Describes a specific function's body fingerprint. diff --git a/toolchain/lower/testdata/builtins/no_op.carbon b/toolchain/lower/testdata/builtins/no_op.carbon index 8826f7b8fb90..2627b09d5aea 100644 --- a/toolchain/lower/testdata/builtins/no_op.carbon +++ b/toolchain/lower/testdata/builtins/no_op.carbon @@ -45,26 +45,26 @@ fn J() { // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CG.Main() #0 !dbg !8 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %a.var = alloca {}, align 8, !dbg !12 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !12 -// CHECK:STDOUT: ret void, !dbg !13 +// CHECK:STDOUT: %a.var = alloca {}, align 8, !dbg !9 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !9 +// CHECK:STDOUT: ret void, !dbg !10 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CH.Main() #0 !dbg !14 { +// CHECK:STDOUT: define void @_CH.Main() #0 !dbg !11 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !15 +// CHECK:STDOUT: ret void, !dbg !12 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: declare void @_CI.Main() // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CJ.Main() #0 !dbg !16 { +// CHECK:STDOUT: define void @_CJ.Main() #0 !dbg !13 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc33_11.1.temp = alloca {}, align 8, !dbg !17 -// CHECK:STDOUT: call void @_CI.Main(), !dbg !17 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc33_11.1.temp), !dbg !17 -// CHECK:STDOUT: ret void, !dbg !18 +// CHECK:STDOUT: %.loc33_11.1.temp = alloca {}, align 8, !dbg !14 +// CHECK:STDOUT: call void @_CI.Main(), !dbg !14 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc33_11.1.temp), !dbg !14 +// CHECK:STDOUT: ret void, !dbg !15 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) @@ -87,14 +87,11 @@ fn J() { // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) // CHECK:STDOUT: !6 = !{null} // CHECK:STDOUT: !7 = !DILocation(line: 17, column: 1, scope: !4) -// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 21, type: !9, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !9 = !DISubroutineType(types: !10) -// CHECK:STDOUT: !10 = !{!11} -// CHECK:STDOUT: !11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 8) -// CHECK:STDOUT: !12 = !DILocation(line: 22, column: 3, scope: !8) -// CHECK:STDOUT: !13 = !DILocation(line: 23, column: 3, scope: !8) -// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "H", linkageName: "_CH.Main", scope: null, file: !3, line: 26, type: !9, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !15 = !DILocation(line: 27, column: 3, scope: !14) -// CHECK:STDOUT: !16 = distinct !DISubprogram(name: "J", linkageName: "_CJ.Main", scope: null, file: !3, line: 32, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !17 = !DILocation(line: 33, column: 9, scope: !16) -// CHECK:STDOUT: !18 = !DILocation(line: 32, column: 1, scope: !16) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 21, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 22, column: 3, scope: !8) +// CHECK:STDOUT: !10 = !DILocation(line: 23, column: 3, scope: !8) +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "H", linkageName: "_CH.Main", scope: null, file: !3, line: 26, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !12 = !DILocation(line: 27, column: 3, scope: !11) +// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "J", linkageName: "_CJ.Main", scope: null, file: !3, line: 32, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !14 = !DILocation(line: 33, column: 9, scope: !13) +// CHECK:STDOUT: !15 = !DILocation(line: 32, column: 1, scope: !13) diff --git a/toolchain/lower/testdata/class/generic.carbon b/toolchain/lower/testdata/class/generic.carbon index 53c15da060a6..4ab08d2e851a 100644 --- a/toolchain/lower/testdata/class/generic.carbon +++ b/toolchain/lower/testdata/class/generic.carbon @@ -314,28 +314,28 @@ fn AccessTuple() -> (i32, i32, i32) { // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CAccessEmpty.Main() #0 !dbg !20 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %c.var = alloca { i1, {} }, align 8, !dbg !21 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !21 -// CHECK:STDOUT: %.loc26_37.2.v = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 0, !dbg !22 -// CHECK:STDOUT: %.loc26_37.4.w = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 1, !dbg !22 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @C.val.8d5.loc26_3, i64 1, i1 false), !dbg !21 -// CHECK:STDOUT: call void @_CGetT.C.Main.335f6bfc3bd91486(ptr %c.var), !dbg !23 -// CHECK:STDOUT: ret void, !dbg !24 +// CHECK:STDOUT: %c.var = alloca { i1, {} }, align 8, !dbg !23 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !23 +// CHECK:STDOUT: %.loc26_37.2.v = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 0, !dbg !24 +// CHECK:STDOUT: %.loc26_37.4.w = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 1, !dbg !24 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @C.val.8d5.loc26_3, i64 1, i1 false), !dbg !23 +// CHECK:STDOUT: call void @_CGetT.C.Main.335f6bfc3bd91486(ptr %c.var), !dbg !25 +// CHECK:STDOUT: ret void, !dbg !26 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CAccessTuple.Main(ptr sret({ i32, i32, i32 }) %return) #0 !dbg !25 { +// CHECK:STDOUT: define void @_CAccessTuple.Main(ptr sret({ i32, i32, i32 }) %return) #0 !dbg !27 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %c.var = alloca { i1, { i32, i32, i32 } }, align 8, !dbg !26 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !26 -// CHECK:STDOUT: %.loc31_57.2.v = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %c.var, i32 0, i32 0, !dbg !27 -// CHECK:STDOUT: %.loc31_57.4.w = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %c.var, i32 0, i32 1, !dbg !27 -// CHECK:STDOUT: %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc31_57.4.w, i32 0, i32 0, !dbg !28 -// CHECK:STDOUT: %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc31_57.4.w, i32 0, i32 1, !dbg !28 -// CHECK:STDOUT: %tuple.elem2.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc31_57.4.w, i32 0, i32 2, !dbg !28 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %c.var, ptr align 4 @C.val.76f.loc31_3, i64 16, i1 false), !dbg !26 -// CHECK:STDOUT: call void @_CGetT.C.Main.3ae0849a77989a11(ptr %return, ptr %c.var), !dbg !29 -// CHECK:STDOUT: ret void, !dbg !30 +// CHECK:STDOUT: %c.var = alloca { i1, { i32, i32, i32 } }, align 8, !dbg !28 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !28 +// CHECK:STDOUT: %.loc31_57.2.v = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %c.var, i32 0, i32 0, !dbg !29 +// CHECK:STDOUT: %.loc31_57.4.w = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %c.var, i32 0, i32 1, !dbg !29 +// CHECK:STDOUT: %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc31_57.4.w, i32 0, i32 0, !dbg !30 +// CHECK:STDOUT: %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc31_57.4.w, i32 0, i32 1, !dbg !30 +// CHECK:STDOUT: %tuple.elem2.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc31_57.4.w, i32 0, i32 2, !dbg !30 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %c.var, ptr align 4 @C.val.76f.loc31_3, i64 16, i1 false), !dbg !28 +// CHECK:STDOUT: call void @_CGetT.C.Main.3ae0849a77989a11(ptr %return, ptr %c.var), !dbg !31 +// CHECK:STDOUT: ret void, !dbg !32 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) @@ -345,52 +345,52 @@ fn AccessTuple() -> (i32, i32, i32) { // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2 // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr i1 @_CGetBool.C.Main.64ccbb8e5d9a0b8e(ptr %self) #0 !dbg !31 { +// CHECK:STDOUT: define linkonce_odr i1 @_CGetBool.C.Main.64ccbb8e5d9a0b8e(ptr %self) #0 !dbg !33 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc6_16.1.v = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !36 -// CHECK:STDOUT: %.loc6_16.2 = load i8, ptr %.loc6_16.1.v, align 1, !dbg !36 -// CHECK:STDOUT: %.loc6_16.21 = trunc i8 %.loc6_16.2 to i1, !dbg !36 -// CHECK:STDOUT: ret i1 %.loc6_16.21, !dbg !37 +// CHECK:STDOUT: %.loc6_16.1.v = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !38 +// CHECK:STDOUT: %.loc6_16.2 = load i8, ptr %.loc6_16.1.v, align 1, !dbg !38 +// CHECK:STDOUT: %.loc6_16.21 = trunc i8 %.loc6_16.2 to i1, !dbg !38 +// CHECK:STDOUT: ret i1 %.loc6_16.21, !dbg !39 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr i32 @_CGetT.C.Main.64ccbb8e5d9a0b8e(ptr %self) #0 !dbg !38 { +// CHECK:STDOUT: define linkonce_odr i32 @_CGetT.C.Main.64ccbb8e5d9a0b8e(ptr %self) #0 !dbg !40 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc9_16.1.w = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 1, !dbg !43 -// CHECK:STDOUT: %.loc9_16.2 = load i32, ptr %.loc9_16.1.w, align 4, !dbg !43 -// CHECK:STDOUT: ret i32 %.loc9_16.2, !dbg !44 +// CHECK:STDOUT: %.loc9_16.1.w = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 1, !dbg !45 +// CHECK:STDOUT: %.loc9_16.2 = load i32, ptr %.loc9_16.1.w, align 4, !dbg !45 +// CHECK:STDOUT: ret i32 %.loc9_16.2, !dbg !46 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CGetT.C.Main.335f6bfc3bd91486(ptr %self) #0 !dbg !45 { +// CHECK:STDOUT: define linkonce_odr void @_CGetT.C.Main.335f6bfc3bd91486(ptr %self) #0 !dbg !47 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc9_16.1.w = getelementptr inbounds nuw { i1, {} }, ptr %self, i32 0, i32 1, !dbg !48 -// CHECK:STDOUT: ret void, !dbg !49 +// CHECK:STDOUT: %.loc9_16.1.w = getelementptr inbounds nuw { i1, {} }, ptr %self, i32 0, i32 1, !dbg !52 +// CHECK:STDOUT: ret void, !dbg !53 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CGetT.C.Main.3ae0849a77989a11(ptr sret({ i32, i32, i32 }) %return, ptr %self) #0 !dbg !50 { +// CHECK:STDOUT: define linkonce_odr void @_CGetT.C.Main.3ae0849a77989a11(ptr sret({ i32, i32, i32 }) %return, ptr %self) #0 !dbg !54 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc9_16.1.w = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %self, i32 0, i32 1, !dbg !53 -// CHECK:STDOUT: call void @"_COp.a02bcb1b1383357d:Copy.Core.975a30a52004fa2c"(ptr %return, ptr %.loc9_16.1.w), !dbg !53 -// CHECK:STDOUT: ret void, !dbg !54 +// CHECK:STDOUT: %.loc9_16.1.w = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %self, i32 0, i32 1, !dbg !57 +// CHECK:STDOUT: call void @"_COp.a02bcb1b1383357d:Copy.Core.975a30a52004fa2c"(ptr %return, ptr %.loc9_16.1.w), !dbg !57 +// CHECK:STDOUT: ret void, !dbg !58 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_COp.a02bcb1b1383357d:Copy.Core.975a30a52004fa2c"(ptr sret({ i32, i32, i32 }) %return, ptr %self) #0 !dbg !55 { -// CHECK:STDOUT: %tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %self, i32 0, i32 0, !dbg !59 -// CHECK:STDOUT: %tuple.elem.load = load i32, ptr %tuple.elem, align 4, !dbg !59 -// CHECK:STDOUT: %tuple.elem1 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 0, !dbg !60 -// CHECK:STDOUT: %tuple.elem2 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %self, i32 0, i32 1, !dbg !61 -// CHECK:STDOUT: %tuple.elem.load3 = load i32, ptr %tuple.elem2, align 4, !dbg !61 -// CHECK:STDOUT: %tuple.elem4 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 1, !dbg !60 -// CHECK:STDOUT: %tuple.elem5 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %self, i32 0, i32 2, !dbg !62 -// CHECK:STDOUT: %tuple.elem.load6 = load i32, ptr %tuple.elem5, align 4, !dbg !62 -// CHECK:STDOUT: %tuple.elem7 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 2, !dbg !60 -// CHECK:STDOUT: store i32 %tuple.elem.load, ptr %tuple.elem1, align 4, !dbg !60 -// CHECK:STDOUT: store i32 %tuple.elem.load3, ptr %tuple.elem4, align 4, !dbg !60 -// CHECK:STDOUT: store i32 %tuple.elem.load6, ptr %tuple.elem7, align 4, !dbg !60 -// CHECK:STDOUT: ret void, !dbg !63 +// CHECK:STDOUT: define linkonce_odr void @"_COp.a02bcb1b1383357d:Copy.Core.975a30a52004fa2c"(ptr sret({ i32, i32, i32 }) %return, ptr %self) #0 !dbg !59 { +// CHECK:STDOUT: %tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %self, i32 0, i32 0, !dbg !63 +// CHECK:STDOUT: %tuple.elem.load = load i32, ptr %tuple.elem, align 4, !dbg !63 +// CHECK:STDOUT: %tuple.elem1 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 0, !dbg !64 +// CHECK:STDOUT: %tuple.elem2 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %self, i32 0, i32 1, !dbg !65 +// CHECK:STDOUT: %tuple.elem.load3 = load i32, ptr %tuple.elem2, align 4, !dbg !65 +// CHECK:STDOUT: %tuple.elem4 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 1, !dbg !64 +// CHECK:STDOUT: %tuple.elem5 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %self, i32 0, i32 2, !dbg !66 +// CHECK:STDOUT: %tuple.elem.load6 = load i32, ptr %tuple.elem5, align 4, !dbg !66 +// CHECK:STDOUT: %tuple.elem7 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 2, !dbg !64 +// CHECK:STDOUT: store i32 %tuple.elem.load, ptr %tuple.elem1, align 4, !dbg !64 +// CHECK:STDOUT: store i32 %tuple.elem.load3, ptr %tuple.elem4, align 4, !dbg !64 +// CHECK:STDOUT: store i32 %tuple.elem.load6, ptr %tuple.elem7, align 4, !dbg !64 +// CHECK:STDOUT: ret void, !dbg !67 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives @@ -424,47 +424,51 @@ fn AccessTuple() -> (i32, i32, i32) { // CHECK:STDOUT: !17 = !DILocation(line: 21, column: 19, scope: !12) // CHECK:STDOUT: !18 = !DILocation(line: 22, column: 10, scope: !12) // CHECK:STDOUT: !19 = !DILocation(line: 22, column: 3, scope: !12) -// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "AccessEmpty", linkageName: "_CAccessEmpty.Main", scope: null, file: !3, line: 25, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !21 = !DILocation(line: 26, column: 3, scope: !20) -// CHECK:STDOUT: !22 = !DILocation(line: 26, column: 18, scope: !20) -// CHECK:STDOUT: !23 = !DILocation(line: 27, column: 10, scope: !20) -// CHECK:STDOUT: !24 = !DILocation(line: 27, column: 3, scope: !20) -// CHECK:STDOUT: !25 = distinct !DISubprogram(name: "AccessTuple", linkageName: "_CAccessTuple.Main", scope: null, file: !3, line: 30, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !26 = !DILocation(line: 31, column: 3, scope: !25) -// CHECK:STDOUT: !27 = !DILocation(line: 31, column: 31, scope: !25) -// CHECK:STDOUT: !28 = !DILocation(line: 31, column: 48, scope: !25) -// CHECK:STDOUT: !29 = !DILocation(line: 32, column: 10, scope: !25) -// CHECK:STDOUT: !30 = !DILocation(line: 32, column: 3, scope: !25) -// CHECK:STDOUT: !31 = distinct !DISubprogram(name: "GetBool", linkageName: "_CGetBool.C.Main.64ccbb8e5d9a0b8e", scope: null, file: !3, line: 5, type: !32, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !34) -// CHECK:STDOUT: !32 = !DISubroutineType(types: !33) -// CHECK:STDOUT: !33 = !{!7, !7} -// CHECK:STDOUT: !34 = !{!35} -// CHECK:STDOUT: !35 = !DILocalVariable(arg: 1, scope: !31, type: !7) -// CHECK:STDOUT: !36 = !DILocation(line: 6, column: 12, scope: !31) -// CHECK:STDOUT: !37 = !DILocation(line: 6, column: 5, scope: !31) -// CHECK:STDOUT: !38 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.64ccbb8e5d9a0b8e", scope: null, file: !3, line: 8, type: !39, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !41) -// CHECK:STDOUT: !39 = !DISubroutineType(types: !40) -// CHECK:STDOUT: !40 = !{!15, !7} -// CHECK:STDOUT: !41 = !{!42} -// CHECK:STDOUT: !42 = !DILocalVariable(arg: 1, scope: !38, type: !7) -// CHECK:STDOUT: !43 = !DILocation(line: 9, column: 12, scope: !38) -// CHECK:STDOUT: !44 = !DILocation(line: 9, column: 5, scope: !38) -// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.335f6bfc3bd91486", scope: null, file: !3, line: 8, type: !32, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !46) -// CHECK:STDOUT: !46 = !{!47} -// CHECK:STDOUT: !47 = !DILocalVariable(arg: 1, scope: !45, type: !7) -// CHECK:STDOUT: !48 = !DILocation(line: 9, column: 12, scope: !45) -// CHECK:STDOUT: !49 = !DILocation(line: 9, column: 5, scope: !45) -// CHECK:STDOUT: !50 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.3ae0849a77989a11", scope: null, file: !3, line: 8, type: !32, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !51) -// CHECK:STDOUT: !51 = !{!52} -// CHECK:STDOUT: !52 = !DILocalVariable(arg: 1, scope: !50, type: !7) -// CHECK:STDOUT: !53 = !DILocation(line: 9, column: 12, scope: !50) -// CHECK:STDOUT: !54 = !DILocation(line: 9, column: 5, scope: !50) -// CHECK:STDOUT: !55 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a02bcb1b1383357d:Copy.Core.975a30a52004fa2c", scope: null, file: !56, line: 54, type: !32, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !57) -// CHECK:STDOUT: !56 = !DIFile(filename: "min_prelude/parts/copy.carbon", directory: "") -// CHECK:STDOUT: !57 = !{!58} -// CHECK:STDOUT: !58 = !DILocalVariable(arg: 1, scope: !55, type: !7) -// CHECK:STDOUT: !59 = !DILocation(line: 55, column: 13, scope: !55) -// CHECK:STDOUT: !60 = !DILocation(line: 55, column: 12, scope: !55) -// CHECK:STDOUT: !61 = !DILocation(line: 55, column: 26, scope: !55) -// CHECK:STDOUT: !62 = !DILocation(line: 55, column: 39, scope: !55) -// CHECK:STDOUT: !63 = !DILocation(line: 55, column: 5, scope: !55) +// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "AccessEmpty", linkageName: "_CAccessEmpty.Main", scope: null, file: !3, line: 25, type: !21, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !21 = !DISubroutineType(types: !22) +// CHECK:STDOUT: !22 = !{null} +// CHECK:STDOUT: !23 = !DILocation(line: 26, column: 3, scope: !20) +// CHECK:STDOUT: !24 = !DILocation(line: 26, column: 18, scope: !20) +// CHECK:STDOUT: !25 = !DILocation(line: 27, column: 10, scope: !20) +// CHECK:STDOUT: !26 = !DILocation(line: 27, column: 3, scope: !20) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "AccessTuple", linkageName: "_CAccessTuple.Main", scope: null, file: !3, line: 30, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !28 = !DILocation(line: 31, column: 3, scope: !27) +// CHECK:STDOUT: !29 = !DILocation(line: 31, column: 31, scope: !27) +// CHECK:STDOUT: !30 = !DILocation(line: 31, column: 48, scope: !27) +// CHECK:STDOUT: !31 = !DILocation(line: 32, column: 10, scope: !27) +// CHECK:STDOUT: !32 = !DILocation(line: 32, column: 3, scope: !27) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "GetBool", linkageName: "_CGetBool.C.Main.64ccbb8e5d9a0b8e", scope: null, file: !3, line: 5, type: !34, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !36) +// CHECK:STDOUT: !34 = !DISubroutineType(types: !35) +// CHECK:STDOUT: !35 = !{!7, !7} +// CHECK:STDOUT: !36 = !{!37} +// CHECK:STDOUT: !37 = !DILocalVariable(arg: 1, scope: !33, type: !7) +// CHECK:STDOUT: !38 = !DILocation(line: 6, column: 12, scope: !33) +// CHECK:STDOUT: !39 = !DILocation(line: 6, column: 5, scope: !33) +// CHECK:STDOUT: !40 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.64ccbb8e5d9a0b8e", scope: null, file: !3, line: 8, type: !41, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !43) +// CHECK:STDOUT: !41 = !DISubroutineType(types: !42) +// CHECK:STDOUT: !42 = !{!15, !7} +// CHECK:STDOUT: !43 = !{!44} +// CHECK:STDOUT: !44 = !DILocalVariable(arg: 1, scope: !40, type: !7) +// CHECK:STDOUT: !45 = !DILocation(line: 9, column: 12, scope: !40) +// CHECK:STDOUT: !46 = !DILocation(line: 9, column: 5, scope: !40) +// CHECK:STDOUT: !47 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.335f6bfc3bd91486", scope: null, file: !3, line: 8, type: !48, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !50) +// CHECK:STDOUT: !48 = !DISubroutineType(types: !49) +// CHECK:STDOUT: !49 = !{null, !7} +// CHECK:STDOUT: !50 = !{!51} +// CHECK:STDOUT: !51 = !DILocalVariable(arg: 1, scope: !47, type: !7) +// CHECK:STDOUT: !52 = !DILocation(line: 9, column: 12, scope: !47) +// CHECK:STDOUT: !53 = !DILocation(line: 9, column: 5, scope: !47) +// CHECK:STDOUT: !54 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.3ae0849a77989a11", scope: null, file: !3, line: 8, type: !34, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !55) +// CHECK:STDOUT: !55 = !{!56} +// CHECK:STDOUT: !56 = !DILocalVariable(arg: 1, scope: !54, type: !7) +// CHECK:STDOUT: !57 = !DILocation(line: 9, column: 12, scope: !54) +// CHECK:STDOUT: !58 = !DILocation(line: 9, column: 5, scope: !54) +// CHECK:STDOUT: !59 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a02bcb1b1383357d:Copy.Core.975a30a52004fa2c", scope: null, file: !60, line: 54, type: !34, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !61) +// CHECK:STDOUT: !60 = !DIFile(filename: "min_prelude/parts/copy.carbon", directory: "") +// CHECK:STDOUT: !61 = !{!62} +// CHECK:STDOUT: !62 = !DILocalVariable(arg: 1, scope: !59, type: !7) +// CHECK:STDOUT: !63 = !DILocation(line: 55, column: 13, scope: !59) +// CHECK:STDOUT: !64 = !DILocation(line: 55, column: 12, scope: !59) +// CHECK:STDOUT: !65 = !DILocation(line: 55, column: 26, scope: !59) +// CHECK:STDOUT: !66 = !DILocation(line: 55, column: 39, scope: !59) +// CHECK:STDOUT: !67 = !DILocation(line: 55, column: 5, scope: !59) diff --git a/toolchain/lower/testdata/for/bindings.carbon b/toolchain/lower/testdata/for/bindings.carbon index 9bcd439544fb..25f434ef3f01 100644 --- a/toolchain/lower/testdata/for/bindings.carbon +++ b/toolchain/lower/testdata/for/bindings.carbon @@ -95,55 +95,55 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.eeebe1ada0072aea(ptr %self) #0 !dbg !28 { -// CHECK:STDOUT: %1 = call i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %self), !dbg !32 -// CHECK:STDOUT: ret i1 %1, !dbg !33 +// CHECK:STDOUT: %1 = call i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %self), !dbg !34 +// CHECK:STDOUT: ret i1 %1, !dbg !35 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CGet.Optional.Core.eeebe1ada0072aea(ptr sret({ i32, i32 }) %return, ptr %self) #0 !dbg !34 { -// CHECK:STDOUT: call void @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %return, ptr %self), !dbg !37 -// CHECK:STDOUT: ret void, !dbg !38 +// CHECK:STDOUT: define linkonce_odr void @_CGet.Optional.Core.eeebe1ada0072aea(ptr sret({ i32, i32 }) %return, ptr %self) #0 !dbg !36 { +// CHECK:STDOUT: call void @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %return, ptr %self), !dbg !39 +// CHECK:STDOUT: ret void, !dbg !40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.eeebe1ada0072aea(ptr sret({ { i32, i32 }, i1 }) %return) #0 !dbg !39 { -// CHECK:STDOUT: call void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %return), !dbg !42 -// CHECK:STDOUT: ret void, !dbg !43 +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.eeebe1ada0072aea(ptr sret({ { i32, i32 }, i1 }) %return) #0 !dbg !41 { +// CHECK:STDOUT: call void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %return), !dbg !44 +// CHECK:STDOUT: ret void, !dbg !45 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %value) #0 !dbg !44 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %value, i32 0, i32 1, !dbg !47 -// CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !47 -// CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !47 -// CHECK:STDOUT: ret i1 %2, !dbg !48 +// CHECK:STDOUT: define linkonce_odr i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr %value) #0 !dbg !46 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %value, i32 0, i32 1, !dbg !49 +// CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !49 +// CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !49 +// CHECK:STDOUT: ret i1 %2, !dbg !50 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr sret({ i32, i32 }) %return, ptr %value) #0 !dbg !49 { -// CHECK:STDOUT: %value1 = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %value, i32 0, i32 0, !dbg !52 -// CHECK:STDOUT: call void @"_COp.e4daa70d026fdea2:Copy.Core.3e36085cb869f630"(ptr %return, ptr %value1), !dbg !52 -// CHECK:STDOUT: ret void, !dbg !53 +// CHECK:STDOUT: define linkonce_odr void @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr sret({ i32, i32 }) %return, ptr %value) #0 !dbg !51 { +// CHECK:STDOUT: %value1 = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %value, i32 0, i32 0, !dbg !54 +// CHECK:STDOUT: call void @"_COp.e4daa70d026fdea2:Copy.Core.3e36085cb869f630"(ptr %return, ptr %value1), !dbg !54 +// CHECK:STDOUT: ret void, !dbg !55 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr sret({ { i32, i32 }, i1 }) %return) #0 !dbg !54 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %return, i32 0, i32 1, !dbg !55 -// CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !55 -// CHECK:STDOUT: ret void, !dbg !56 +// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d"(ptr sret({ { i32, i32 }, i1 }) %return) #0 !dbg !56 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %return, i32 0, i32 1, !dbg !57 +// CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !57 +// CHECK:STDOUT: ret void, !dbg !58 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_COp.e4daa70d026fdea2:Copy.Core.3e36085cb869f630"(ptr sret({ i32, i32 }) %return, ptr %self) #0 !dbg !57 { -// CHECK:STDOUT: %tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 0, !dbg !61 -// CHECK:STDOUT: %tuple.elem.load = load i32, ptr %tuple.elem, align 4, !dbg !61 -// CHECK:STDOUT: %tuple.elem1 = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !62 -// CHECK:STDOUT: %tuple.elem2 = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 1, !dbg !63 -// CHECK:STDOUT: %tuple.elem.load3 = load i32, ptr %tuple.elem2, align 4, !dbg !63 -// CHECK:STDOUT: %tuple.elem4 = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !62 -// CHECK:STDOUT: store i32 %tuple.elem.load, ptr %tuple.elem1, align 4, !dbg !62 -// CHECK:STDOUT: store i32 %tuple.elem.load3, ptr %tuple.elem4, align 4, !dbg !62 -// CHECK:STDOUT: ret void, !dbg !64 +// CHECK:STDOUT: define linkonce_odr void @"_COp.e4daa70d026fdea2:Copy.Core.3e36085cb869f630"(ptr sret({ i32, i32 }) %return, ptr %self) #0 !dbg !59 { +// CHECK:STDOUT: %tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 0, !dbg !63 +// CHECK:STDOUT: %tuple.elem.load = load i32, ptr %tuple.elem, align 4, !dbg !63 +// CHECK:STDOUT: %tuple.elem1 = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !64 +// CHECK:STDOUT: %tuple.elem2 = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 1, !dbg !65 +// CHECK:STDOUT: %tuple.elem.load3 = load i32, ptr %tuple.elem2, align 4, !dbg !65 +// CHECK:STDOUT: %tuple.elem4 = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !64 +// CHECK:STDOUT: store i32 %tuple.elem.load, ptr %tuple.elem1, align 4, !dbg !64 +// CHECK:STDOUT: store i32 %tuple.elem.load3, ptr %tuple.elem4, align 4, !dbg !64 +// CHECK:STDOUT: ret void, !dbg !66 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives @@ -171,7 +171,7 @@ fn For() { // CHECK:STDOUT: !12 = !DILocation(line: 26, column: 1, scope: !4) // CHECK:STDOUT: !13 = distinct !DISubprogram(name: "NewCursor", linkageName: "_CNewCursor.EmptyRange.1ccc388edee5a786.Main:Iterate.Core.c6c37881d2ed7d9d", scope: null, file: !3, line: 15, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !17) // CHECK:STDOUT: !14 = !DISubroutineType(types: !15) -// CHECK:STDOUT: !15 = !{!16, !16} +// CHECK:STDOUT: !15 = !{null, !16} // CHECK:STDOUT: !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 8) // CHECK:STDOUT: !17 = !{!18} // CHECK:STDOUT: !18 = !DILocalVariable(arg: 1, scope: !13, type: !16) @@ -184,40 +184,42 @@ fn For() { // CHECK:STDOUT: !25 = !DILocalVariable(arg: 2, scope: !20, type: !16) // CHECK:STDOUT: !26 = !DILocation(line: 19, column: 14, scope: !20) // CHECK:STDOUT: !27 = !DILocation(line: 19, column: 7, scope: !20) -// CHECK:STDOUT: !28 = distinct !DISubprogram(name: "HasValue", linkageName: "_CHasValue.Optional.Core.eeebe1ada0072aea", scope: null, file: !29, line: 32, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !30) +// CHECK:STDOUT: !28 = distinct !DISubprogram(name: "HasValue", linkageName: "_CHasValue.Optional.Core.eeebe1ada0072aea", scope: null, file: !29, line: 32, type: !30, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !32) // CHECK:STDOUT: !29 = !DIFile(filename: "{{.*}}/prelude/types/optional.carbon", directory: "") -// CHECK:STDOUT: !30 = !{!31} -// CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !28, type: !16) -// CHECK:STDOUT: !32 = !DILocation(line: 33, column: 12, scope: !28) -// CHECK:STDOUT: !33 = !DILocation(line: 33, column: 5, scope: !28) -// CHECK:STDOUT: !34 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.Optional.Core.eeebe1ada0072aea", scope: null, file: !29, line: 35, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !35) -// CHECK:STDOUT: !35 = !{!36} -// CHECK:STDOUT: !36 = !DILocalVariable(arg: 1, scope: !34, type: !16) -// CHECK:STDOUT: !37 = !DILocation(line: 36, column: 12, scope: !34) -// CHECK:STDOUT: !38 = !DILocation(line: 36, column: 5, scope: !34) -// CHECK:STDOUT: !39 = distinct !DISubprogram(name: "None", linkageName: "_CNone.Optional.Core.eeebe1ada0072aea", scope: null, file: !29, line: 26, type: !40, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !40 = !DISubroutineType(types: !41) -// CHECK:STDOUT: !41 = !{!16} -// CHECK:STDOUT: !42 = !DILocation(line: 27, column: 12, scope: !39) -// CHECK:STDOUT: !43 = !DILocation(line: 27, column: 5, scope: !39) -// CHECK:STDOUT: !44 = distinct !DISubprogram(name: "Has", linkageName: "_CHas.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d", scope: null, file: !29, line: 123, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !45) -// CHECK:STDOUT: !45 = !{!46} -// CHECK:STDOUT: !46 = !DILocalVariable(arg: 1, scope: !44, type: !16) -// CHECK:STDOUT: !47 = !DILocation(line: 124, column: 12, scope: !44) -// CHECK:STDOUT: !48 = !DILocation(line: 124, column: 5, scope: !44) -// CHECK:STDOUT: !49 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d", scope: null, file: !29, line: 126, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !50) -// CHECK:STDOUT: !50 = !{!51} -// CHECK:STDOUT: !51 = !DILocalVariable(arg: 1, scope: !49, type: !16) -// CHECK:STDOUT: !52 = !DILocation(line: 127, column: 12, scope: !49) -// CHECK:STDOUT: !53 = !DILocation(line: 127, column: 5, scope: !49) -// CHECK:STDOUT: !54 = distinct !DISubprogram(name: "None", linkageName: "_CNone.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d", scope: null, file: !29, line: 110, type: !40, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !55 = !DILocation(line: 112, column: 5, scope: !54) -// CHECK:STDOUT: !56 = !DILocation(line: 113, column: 5, scope: !54) -// CHECK:STDOUT: !57 = distinct !DISubprogram(name: "Op", linkageName: "_COp.e4daa70d026fdea2:Copy.Core.3e36085cb869f630", scope: null, file: !58, line: 58, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !59) -// CHECK:STDOUT: !58 = !DIFile(filename: "{{.*}}/prelude/copy.carbon", directory: "") -// CHECK:STDOUT: !59 = !{!60} -// CHECK:STDOUT: !60 = !DILocalVariable(arg: 1, scope: !57, type: !16) -// CHECK:STDOUT: !61 = !DILocation(line: 59, column: 13, scope: !57) -// CHECK:STDOUT: !62 = !DILocation(line: 59, column: 12, scope: !57) -// CHECK:STDOUT: !63 = !DILocation(line: 59, column: 26, scope: !57) -// CHECK:STDOUT: !64 = !DILocation(line: 59, column: 5, scope: !57) +// CHECK:STDOUT: !30 = !DISubroutineType(types: !31) +// CHECK:STDOUT: !31 = !{!16, !16} +// CHECK:STDOUT: !32 = !{!33} +// CHECK:STDOUT: !33 = !DILocalVariable(arg: 1, scope: !28, type: !16) +// CHECK:STDOUT: !34 = !DILocation(line: 33, column: 12, scope: !28) +// CHECK:STDOUT: !35 = !DILocation(line: 33, column: 5, scope: !28) +// CHECK:STDOUT: !36 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.Optional.Core.eeebe1ada0072aea", scope: null, file: !29, line: 35, type: !30, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !37) +// CHECK:STDOUT: !37 = !{!38} +// CHECK:STDOUT: !38 = !DILocalVariable(arg: 1, scope: !36, type: !16) +// CHECK:STDOUT: !39 = !DILocation(line: 36, column: 12, scope: !36) +// CHECK:STDOUT: !40 = !DILocation(line: 36, column: 5, scope: !36) +// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "None", linkageName: "_CNone.Optional.Core.eeebe1ada0072aea", scope: null, file: !29, line: 26, type: !42, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !42 = !DISubroutineType(types: !43) +// CHECK:STDOUT: !43 = !{!16} +// CHECK:STDOUT: !44 = !DILocation(line: 27, column: 12, scope: !41) +// CHECK:STDOUT: !45 = !DILocation(line: 27, column: 5, scope: !41) +// CHECK:STDOUT: !46 = distinct !DISubprogram(name: "Has", linkageName: "_CHas.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d", scope: null, file: !29, line: 123, type: !30, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !47) +// CHECK:STDOUT: !47 = !{!48} +// CHECK:STDOUT: !48 = !DILocalVariable(arg: 1, scope: !46, type: !16) +// CHECK:STDOUT: !49 = !DILocation(line: 124, column: 12, scope: !46) +// CHECK:STDOUT: !50 = !DILocation(line: 124, column: 5, scope: !46) +// CHECK:STDOUT: !51 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d", scope: null, file: !29, line: 126, type: !30, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !52) +// CHECK:STDOUT: !52 = !{!53} +// CHECK:STDOUT: !53 = !DILocalVariable(arg: 1, scope: !51, type: !16) +// CHECK:STDOUT: !54 = !DILocation(line: 127, column: 12, scope: !51) +// CHECK:STDOUT: !55 = !DILocation(line: 127, column: 5, scope: !51) +// CHECK:STDOUT: !56 = distinct !DISubprogram(name: "None", linkageName: "_CNone.3e8267224c5dc9c2:OptionalStorage.Core.c6c37881d2ed7d9d", scope: null, file: !29, line: 110, type: !42, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !57 = !DILocation(line: 112, column: 5, scope: !56) +// CHECK:STDOUT: !58 = !DILocation(line: 113, column: 5, scope: !56) +// CHECK:STDOUT: !59 = distinct !DISubprogram(name: "Op", linkageName: "_COp.e4daa70d026fdea2:Copy.Core.3e36085cb869f630", scope: null, file: !60, line: 58, type: !30, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !61) +// CHECK:STDOUT: !60 = !DIFile(filename: "{{.*}}/prelude/copy.carbon", directory: "") +// CHECK:STDOUT: !61 = !{!62} +// CHECK:STDOUT: !62 = !DILocalVariable(arg: 1, scope: !59, type: !16) +// CHECK:STDOUT: !63 = !DILocation(line: 59, column: 13, scope: !59) +// CHECK:STDOUT: !64 = !DILocation(line: 59, column: 12, scope: !59) +// CHECK:STDOUT: !65 = !DILocation(line: 59, column: 26, scope: !59) +// CHECK:STDOUT: !66 = !DILocation(line: 59, column: 5, scope: !59) diff --git a/toolchain/lower/testdata/function/call/empty_struct.carbon b/toolchain/lower/testdata/function/call/empty_struct.carbon index de8d013c796e..0cdf8ab7f538 100644 --- a/toolchain/lower/testdata/function/call/empty_struct.carbon +++ b/toolchain/lower/testdata/function/call/empty_struct.carbon @@ -24,16 +24,16 @@ fn Main() { // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CEcho.Main() #0 !dbg !4 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: ret void, !dbg !7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CMain.Main() #0 !dbg !9 { +// CHECK:STDOUT: define void @_CMain.Main() #0 !dbg !8 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %_.var = alloca {}, align 8, !dbg !12 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !12 -// CHECK:STDOUT: call void @_CEcho.Main(), !dbg !13 -// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: %_.var = alloca {}, align 8, !dbg !9 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !9 +// CHECK:STDOUT: call void @_CEcho.Main(), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) @@ -51,12 +51,9 @@ fn Main() { // CHECK:STDOUT: !3 = !DIFile(filename: "empty_struct.carbon", directory: "") // CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Echo", linkageName: "_CEcho.Main", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) -// CHECK:STDOUT: !6 = !{!7} -// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 8) -// CHECK:STDOUT: !8 = !DILocation(line: 14, column: 3, scope: !4) -// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 17, type: !10, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !10 = !DISubroutineType(types: !11) -// CHECK:STDOUT: !11 = !{null} -// CHECK:STDOUT: !12 = !DILocation(line: 18, column: 3, scope: !9) -// CHECK:STDOUT: !13 = !DILocation(line: 18, column: 15, scope: !9) -// CHECK:STDOUT: !14 = !DILocation(line: 17, column: 1, scope: !9) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 14, column: 3, scope: !4) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 18, column: 3, scope: !8) +// CHECK:STDOUT: !10 = !DILocation(line: 18, column: 15, scope: !8) +// CHECK:STDOUT: !11 = !DILocation(line: 17, column: 1, scope: !8) diff --git a/toolchain/lower/testdata/function/call/empty_tuple.carbon b/toolchain/lower/testdata/function/call/empty_tuple.carbon index 7977539275be..9aed5eb11d4e 100644 --- a/toolchain/lower/testdata/function/call/empty_tuple.carbon +++ b/toolchain/lower/testdata/function/call/empty_tuple.carbon @@ -24,16 +24,16 @@ fn Main() { // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CEcho.Main() #0 !dbg !4 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: ret void, !dbg !7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CMain.Main() #0 !dbg !9 { +// CHECK:STDOUT: define void @_CMain.Main() #0 !dbg !8 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %_.var = alloca {}, align 8, !dbg !12 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !12 -// CHECK:STDOUT: call void @_CEcho.Main(), !dbg !13 -// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: %_.var = alloca {}, align 8, !dbg !9 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !9 +// CHECK:STDOUT: call void @_CEcho.Main(), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) @@ -51,12 +51,9 @@ fn Main() { // CHECK:STDOUT: !3 = !DIFile(filename: "empty_tuple.carbon", directory: "") // CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Echo", linkageName: "_CEcho.Main", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) -// CHECK:STDOUT: !6 = !{!7} -// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 8) -// CHECK:STDOUT: !8 = !DILocation(line: 14, column: 3, scope: !4) -// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 17, type: !10, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !10 = !DISubroutineType(types: !11) -// CHECK:STDOUT: !11 = !{null} -// CHECK:STDOUT: !12 = !DILocation(line: 18, column: 3, scope: !9) -// CHECK:STDOUT: !13 = !DILocation(line: 18, column: 15, scope: !9) -// CHECK:STDOUT: !14 = !DILocation(line: 17, column: 1, scope: !9) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 14, column: 3, scope: !4) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 18, column: 3, scope: !8) +// CHECK:STDOUT: !10 = !DILocation(line: 18, column: 15, scope: !8) +// CHECK:STDOUT: !11 = !DILocation(line: 17, column: 1, scope: !8) diff --git a/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon b/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon index a3d9364af59e..128cc0cd7b83 100644 --- a/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon +++ b/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon @@ -31,19 +31,19 @@ fn Main() { // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CBar.Main() #0 !dbg !8 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !12 +// CHECK:STDOUT: ret void, !dbg !9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CMain.Main() #0 !dbg !13 { +// CHECK:STDOUT: define void @_CMain.Main() #0 !dbg !10 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %_.var = alloca {}, align 8, !dbg !14 -// CHECK:STDOUT: %.loc19_23.1.temp = alloca {}, align 8, !dbg !15 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !14 -// CHECK:STDOUT: call void @_CFoo.Main(), !dbg !15 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc19_23.1.temp), !dbg !15 -// CHECK:STDOUT: call void @_CBar.Main(), !dbg !16 -// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: %_.var = alloca {}, align 8, !dbg !11 +// CHECK:STDOUT: %.loc19_23.1.temp = alloca {}, align 8, !dbg !12 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !11 +// CHECK:STDOUT: call void @_CFoo.Main(), !dbg !12 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc19_23.1.temp), !dbg !12 +// CHECK:STDOUT: call void @_CBar.Main(), !dbg !13 +// CHECK:STDOUT: ret void, !dbg !14 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) @@ -66,13 +66,10 @@ fn Main() { // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) // CHECK:STDOUT: !6 = !{null} // CHECK:STDOUT: !7 = !DILocation(line: 14, column: 1, scope: !4) -// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "Bar", linkageName: "_CBar.Main", scope: null, file: !3, line: 16, type: !9, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !9 = !DISubroutineType(types: !10) -// CHECK:STDOUT: !10 = !{!11} -// CHECK:STDOUT: !11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 8) -// CHECK:STDOUT: !12 = !DILocation(line: 16, column: 23, scope: !8) -// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !14 = !DILocation(line: 19, column: 3, scope: !13) -// CHECK:STDOUT: !15 = !DILocation(line: 19, column: 19, scope: !13) -// CHECK:STDOUT: !16 = !DILocation(line: 19, column: 15, scope: !13) -// CHECK:STDOUT: !17 = !DILocation(line: 18, column: 1, scope: !13) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "Bar", linkageName: "_CBar.Main", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 16, column: 23, scope: !8) +// CHECK:STDOUT: !10 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !11 = !DILocation(line: 19, column: 3, scope: !10) +// CHECK:STDOUT: !12 = !DILocation(line: 19, column: 19, scope: !10) +// CHECK:STDOUT: !13 = !DILocation(line: 19, column: 15, scope: !10) +// CHECK:STDOUT: !14 = !DILocation(line: 18, column: 1, scope: !10) diff --git a/toolchain/lower/testdata/function/call/ref_return.carbon b/toolchain/lower/testdata/function/call/ref_return.carbon new file mode 100644 index 000000000000..7d968f9bd3e7 --- /dev/null +++ b/toolchain/lower/testdata/function/call/ref_return.carbon @@ -0,0 +1,68 @@ +// 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-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/function/call/ref_return.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/function/call/ref_return.carbon + +fn Echo(ref x: i32) -> ref i32 { + return x; +} + +fn Main() { + var a: i32 = 0; + Echo(ref a) = 1; +} + +// CHECK:STDOUT: ; ModuleID = 'ref_return.carbon' +// CHECK:STDOUT: source_filename = "ref_return.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define ptr @_CEcho.Main(ptr %x) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret ptr %x, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMain.Main() #0 !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca i32, align 4, !dbg !15 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !15 +// CHECK:STDOUT: store i32 0, ptr %a.var, align 4, !dbg !15 +// CHECK:STDOUT: %Echo.call = call ptr @_CEcho.Main(ptr %a.var), !dbg !16 +// CHECK:STDOUT: store i32 1, ptr %Echo.call, align 4, !dbg !16 +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "ref_return.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Echo", linkageName: "_CEcho.Main", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !9) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{!7, !8} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 8) +// CHECK:STDOUT: !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !9 = !{!10} +// CHECK:STDOUT: !10 = !DILocalVariable(arg: 1, scope: !4, type: !8) +// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 3, scope: !4) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 17, type: !13, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DISubroutineType(types: !14) +// CHECK:STDOUT: !14 = !{null} +// CHECK:STDOUT: !15 = !DILocation(line: 18, column: 3, scope: !12) +// CHECK:STDOUT: !16 = !DILocation(line: 19, column: 3, scope: !12) +// CHECK:STDOUT: !17 = !DILocation(line: 17, column: 1, scope: !12) diff --git a/toolchain/lower/testdata/function/generic/type_representation.carbon b/toolchain/lower/testdata/function/generic/type_representation.carbon index 5c3929c34e81..739d2cfa8985 100644 --- a/toolchain/lower/testdata/function/generic/type_representation.carbon +++ b/toolchain/lower/testdata/function/generic/type_representation.carbon @@ -262,7 +262,7 @@ fn F_nested_tuple(a: ((i32, i32), X)) -> ((i32, i32), X) { // CHECK:STDOUT: !30 = !DILocation(line: 44, column: 3, scope: !26) // CHECK:STDOUT: !31 = distinct !DISubprogram(name: "Op", linkageName: "_COp.61ea2aba74ab3bf1:Copy.Main", scope: null, file: !3, line: 48, type: !32, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !32 = !DISubroutineType(types: !33) -// CHECK:STDOUT: !33 = !{!19} +// CHECK:STDOUT: !33 = !{null} // CHECK:STDOUT: !34 = !DILocation(line: 49, column: 5, scope: !31) // CHECK:STDOUT: !35 = distinct !DISubprogram(name: "F_empty_tuple", linkageName: "_CF_empty_tuple.Main", scope: null, file: !3, line: 53, type: !32, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !36 = !DILocation(line: 54, column: 10, scope: !35) diff --git a/toolchain/sem_ir/function.cpp b/toolchain/sem_ir/function.cpp index ec624693878a..5841b92fa020 100644 --- a/toolchain/sem_ir/function.cpp +++ b/toolchain/sem_ir/function.cpp @@ -112,25 +112,6 @@ auto DecomposeVirtualFunction(const File& sem_ir, InstId fn_decl_id, .specific_id = specific_id}; } -auto Function::GetParamPatternInfoFromPatternId(const File& sem_ir, - InstId pattern_id) - -> std::optional { - auto inst_id = pattern_id; - auto inst = sem_ir.insts().Get(inst_id); - - sem_ir.insts().TryUnwrap(inst, inst_id, &VarPattern::subpattern_id); - auto [param_pattern, param_pattern_id] = - sem_ir.insts().TryUnwrap(inst, inst_id, &AnyParamPattern::subpattern_id); - if (!param_pattern) { - return std::nullopt; - } - - auto binding_pattern = inst.As(); - return {{.inst_id = param_pattern_id, - .inst = *param_pattern, - .entity_name_id = binding_pattern.entity_name_id}}; -} - auto Function::GetDeclaredReturnType(const File& file, SpecificId specific_id) const -> TypeId { if (!return_type_inst_id.has_value()) { diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index 79a0c7d55d43..bdf33eff72fe 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -163,17 +163,6 @@ struct Function : public EntityWithParamsBase, : InstId::None; } - // Given the ID of an instruction from `param_patterns_id` or - // `implicit_param_patterns_id`, returns a `ParamPatternInfo` value with the - // corresponding `Call` parameter pattern, its ID, and the entity_name_id of - // the underlying binding pattern, or std::nullopt if there is no - // corresponding `Call` parameter. - // TODO: Remove this, by exposing `Call` parameter patterns instead of `Call` - // parameters in EntityWithParams. - static auto GetParamPatternInfoFromPatternId(const File& sem_ir, - InstId param_pattern_id) - -> std::optional; - // Gets the declared return type for a specific version of this function, or // the canonical return type for the original declaration no specific is // specified. Returns `None` if no return type was specified, in which