From 7215302a27ddfa7dd51eddc6cba4218a0543554d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 24 Jun 2025 10:22:48 -0700 Subject: [PATCH] Clean up and extend support for cross-file lowering of specific functions. (#5688) Update remaining parts of lowering, in particular the lowering of aggregates, to handle lowering within a specific from a different file than its generic. Look up information about a type in the current specific and in its file rather than performing lookups for the type in the generic and its file. Remove or fix all remaining uses of raw `TypeId` in lower/function_context and lower/handle*, so that the type from the specific is consistently always used when lowering a specific function. --------- Co-authored-by: Geoff Romer --- toolchain/docs/lower.md | 116 ++++++- toolchain/lower/file_context.cpp | 10 +- toolchain/lower/function_context.cpp | 73 +++-- toolchain/lower/function_context.h | 96 ++++-- toolchain/lower/handle.cpp | 62 ++-- toolchain/lower/handle_aggregates.cpp | 140 ++++----- toolchain/lower/handle_call.cpp | 49 ++- toolchain/lower/handle_expr_category.cpp | 31 +- toolchain/lower/testdata/class/generic.carbon | 282 +++++++++++++++++- .../function/generic/call_basic.carbon | 2 +- 10 files changed, 672 insertions(+), 189 deletions(-) diff --git a/toolchain/docs/lower.md b/toolchain/docs/lower.md index af7dd221a3b1..f35b963ec943 100644 --- a/toolchain/docs/lower.md +++ b/toolchain/docs/lower.md @@ -11,6 +11,9 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Table of contents - [Overview](#overview) +- [Generic lowering](#generic-lowering) +- [Cross-file lowering](#cross-file-lowering) +- [Specific deduplication and fingerprinting](#specific-deduplication-and-fingerprinting) - [Mangling](#mangling) - [Examples](#examples) @@ -22,10 +25,117 @@ Lowering takes the SemIR and produces LLVM IR. At present, this is done in a single pass, although it's possible we may need to do a second pass so that we can first generate type information for function arguments. +The lowering context is split into three layers: + +- The `Context` object holds state for an overall lowering process that + produces a single LLVM module. +- The `FileContext` object holds state for lowering from a particular + `SemIR::File`, and holds a pointer to its enclosing `Context`. Multiple + files may be involved in a single lowering process when lowering a generic, + where the definition of the generic and the specific may be owned by + distinct files. This setup would also allow us to lower an entire library + into a single LLVM module if we chose to do so. +- The `FunctionContext` object holds state for lowering a particular function, + including an `IRBuilder` and mappings from the local `InstId`s to their + lowered `llvm::Value*`s and from the local `InstBlockId`s to their lowered + `llvm::BasicBlock*`s. + Lowering is done per `SemIR::InstBlock`. This minimizes changes to the `IRBuilder` insertion point, something that is both expensive and potentially fragile. +## Generic lowering + +In order to support lowering generic functions, the `FunctionContext` tracks +both the `FunctionId` of the function being lowered and a corresponding +`SpecificId`. Whenever `FunctionContext` or a `HandleInst` function inspects a +property of an instruction that can vary between specifics -- in particular, the +type or constant value of an instruction -- that value is looked up in the +current specific, and the corresponding type or value is used instead. + +`FunctionContext::GetTypeOfInst` and `FunctionContext::GetTypeIdOfInst` do this +mapping for the type of an instruction, and should be used instead of directly +looking at the `type_id` field of a typed instruction throughout function +lowering. Similarly, `FunctionContext::GetValue` does this mapping when looking +up the constant value of an instruction. + +## Cross-file lowering + +`FunctionContext` lowering may draw information used to lower the function from +two different files: + +- The file in which the function was defined. +- For a generic function, the file in which the specific was formed. + +Each of these files has its own `FileContext`, which tracks its corresponding +`SemIR::File`, as well as mappings from its constant values to +`llvm::Constant*`s and mappings from its functions to `llvm::Function*`s, and so +on. + +When querying the type of an instruction using +`FunctionContext::GetTypeIdOfInst`, the resulting type may be owned by either of +these files. The type is represented as a `TypeInFile`, which is a pair of the +owning `SemIR::File*` and the `SemIR::TypeId` within that file. Care must be +taken to only pass the `TypeId` in a `TypeInFile` to code that expects a +`TypeId` within the corresponding `SemIR::File*`. To reduce the risk of errors, +code within `FunctionContext` and `HandleInst` functions should not directly +interact with `TypeId`s, and should instead always use `TypeInFile`. + +Similarly, other type properties have `FunctionContext` wrappers that track the +file that owns the `TypeId`s: + +- `FunctionContext::GetValueRepr` returns a `ValueReprInFile` which is a pair + of a `SemIR::File*` and a `SemIR::ValueRepr`. +- `FunctionContext::GetReturnTypeInfo` returns a `ReturnTypeInfoInFile` which + is a pair of a `SemIR::File*` and a `SemIR::ReturnTypeInfo`. + +These pairs are kept wrapped in the `*InFile` structs wherever possible, in +order to minimize the chance of an ID being used with the wrong file. + +## Specific deduplication and fingerprinting + +Specifics for the same generic are deduplicated by detecting whether we +generated the same LLVM IR for all the portions of the specific that depend on +generic arguments. This is accomplished in part by computing a fingerprint for +each specific. The fingerprint contains: + +- For each symbolic constant value used while lowering, the lowered LLVM value + in the specific. +- For each symbolic type used while lowering, the lowered LLVM type in the + specific. +- For each called function, information about the specific callee. TODO: + Describe how we handle deduplicating strongly-connected components of the + call graph. +- For each other property of the specific that lowering depends on, the value + of that property. + +These fingerprinted values are tracked by the `FunctionContext` accessors that +obtain the information from SemIR: + +- `FunctionContext::GetType` adds the `llvm::Type*` produced for a symbolic + type to the fingerprint. +- `FunctionContext::GetValue` adds the `llvm::Value*` produced for a symbolic + constant to the fingerprint. +- `FunctionContext::GetValueRepr` adds the kind of the value representation, + but not the value representation type, to the fingerprint. +- `FunctionContext::GetInitRepr` adds the kind of the initializing + representation to the fingerprint. +- `FunctionContext::GetReturnTypeInfo` adds the kind of the return + representation, but not the type, to the fingerprint. + +For `GetValueRepr` and `GetReturnTypeInfo`, the corresponding type is +represented as a `TypeInFile`. The convention in use is that `TypeInFile` values +represent types that have not yet been added to the fingerprint for the +specific, and the mapping from `TypeInFile` to `llvm::Type*` is the point where +the type is added to the fingerprint, but other data such as the enumeration +values stored on `ReturnTypeInfoInFile` have already been added to the +fingerprint. + +Additional information queried from SemIR by `FunctionContext` or a `HandleInst` +function should follow the same pattern, adding a getter on `FunctionContext` +that adds the information to the fingerprint, and returns a `*InFile` wrapper +struct if the result contains any `TypeId`s. + ## Mangling Part of lowering is choosing deterministically unique identifiers for each @@ -42,8 +152,10 @@ Otherwise the resulting name consists of: 1. `_C` 2. The unqualified function name (function name mangling is the only thing implemented at the moment). -3. `.` -4. If the function being mangled is a member of: +3. If the function is a thunk, `:thunk` to distinguish it from the function it + invokes. +4. `.` +5. If the function being mangled is a member of: - an `impl`, then add: 1. The implementing type, per the scope mangling. 2. `:` diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 73de32136ff6..d81ce8246495 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -823,16 +823,18 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, // parameter order. auto lower_param = [&](SemIR::InstId param_id) { // Get the value of the parameter from the function argument. - auto param_inst = definition_ir.insts().GetAs(param_id); llvm::Value* param_value; - if (SemIR::ValueRepr::ForType(definition_ir, param_inst.type_id).kind != + // 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.GetTypeOfInstInSpecific(param_id)); + 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); diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 5cbff0f8bfb9..29ee91b5f5f8 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -145,8 +145,8 @@ auto FunctionContext::LowerInst(SemIR::InstId inst_id) -> void { builder_.getInserter().SetCurrentInstId(SemIR::InstId::None); } -auto FunctionContext::GetBlockArg(SemIR::InstBlockId block_id, - SemIR::TypeId type_id) -> llvm::PHINode* { +auto FunctionContext::GetBlockArg(SemIR::InstBlockId block_id, TypeInFile type) + -> llvm::PHINode* { llvm::BasicBlock* block = GetBlock(block_id); // Find the existing phi, if any. @@ -160,7 +160,7 @@ auto FunctionContext::GetBlockArg(SemIR::InstBlockId block_id, // The number of predecessor slots to reserve. static constexpr unsigned NumReservedPredecessors = 2; - auto* phi = llvm::PHINode::Create(GetType(type_id), NumReservedPredecessors); + auto* phi = llvm::PHINode::Create(GetType(type), NumReservedPredecessors); phi->insertInto(block, block->begin()); return phi; } @@ -217,36 +217,60 @@ auto FunctionContext::GetDebugLoc(SemIR::InstId inst_id) -> llvm::DebugLoc { loc.column_number, di_subprogram_); } -auto FunctionContext::FinishInit(SemIR::TypeId type_id, SemIR::InstId dest_id, +auto FunctionContext::FinishInit(TypeInFile type, SemIR::InstId dest_id, SemIR::InstId source_id) -> void { - switch (SemIR::InitRepr::ForType(sem_ir(), type_id).kind) { + switch (GetInitRepr(type).kind) { case SemIR::InitRepr::None: break; case SemIR::InitRepr::InPlace: if (sem_ir().constant_values().Get(source_id).is_constant()) { // When initializing from a constant, emission of the source doesn't // initialize the destination. Copy the constant value instead. - CopyValue(type_id, source_id, dest_id); + CopyValue(type, source_id, dest_id); } break; case SemIR::InitRepr::ByCopy: - CopyValue(type_id, source_id, dest_id); + CopyValue(type, source_id, dest_id); break; case SemIR::InitRepr::Incomplete: CARBON_FATAL("Lowering aggregate initialization of incomplete type {0}", - sem_ir().types().GetAsInst(type_id)); + type.file->types().GetAsInst(type.type_id)); } } -auto FunctionContext::GetTypeIdOfInstInSpecific(SemIR::InstId inst_id) - -> std::pair { - return SemIR::GetTypeOfInstInSpecific(specific_sem_ir(), specific_id(), - sem_ir(), inst_id); +auto FunctionContext::GetTypeIdOfInst(SemIR::InstId inst_id) -> TypeInFile { + auto [file, type_id] = SemIR::GetTypeOfInstInSpecific( + specific_sem_ir(), specific_id(), sem_ir(), inst_id); + return {.file = file, .type_id = type_id}; } -auto FunctionContext::CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id, +auto FunctionContext::GetValueRepr(TypeInFile type) -> ValueReprInFile { + ValueReprInFile result = { + .file = type.file, + .repr = SemIR::ValueRepr::ForType(*type.file, type.type_id)}; + AddEnumToCurrentFingerprint(result.repr.kind); + AddEnumToCurrentFingerprint(result.repr.aggregate_kind); + return result; +} + +auto FunctionContext::GetInitRepr(TypeInFile type) -> SemIR::InitRepr { + auto result = SemIR::InitRepr::ForType(*type.file, type.type_id); + AddEnumToCurrentFingerprint(result.kind); + return result; +} + +auto FunctionContext::GetReturnTypeInfo(TypeInFile type) + -> ReturnTypeInfoInFile { + ReturnTypeInfoInFile result = { + .file = type.file, + .info = SemIR::ReturnTypeInfo::ForType(*type.file, type.type_id)}; + AddEnumToCurrentFingerprint(result.info.init_repr.kind); + return result; +} + +auto FunctionContext::CopyValue(TypeInFile type, SemIR::InstId source_id, SemIR::InstId dest_id) -> void { - switch (auto rep = SemIR::ValueRepr::ForType(sem_ir(), type_id); rep.kind) { + switch (GetValueRepr(type).repr.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL("Attempt to copy incomplete type"); case SemIR::ValueRepr::None: @@ -255,25 +279,25 @@ auto FunctionContext::CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id, builder().CreateStore(GetValue(source_id), GetValue(dest_id)); break; case SemIR::ValueRepr::Pointer: - CopyObject(type_id, source_id, dest_id); + CopyObject(type, source_id, dest_id); break; case SemIR::ValueRepr::Custom: CARBON_FATAL("TODO: Add support for CopyValue with custom value rep"); } } -auto FunctionContext::CopyObject(SemIR::TypeId type_id, SemIR::InstId source_id, +auto FunctionContext::CopyObject(TypeInFile type, SemIR::InstId source_id, SemIR::InstId dest_id) -> void { const auto& layout = llvm_module().getDataLayout(); - auto* type = GetType(type_id); + auto* llvm_type = GetType(type); // TODO: Compute known alignment of the source and destination, which may // be greater than the alignment computed by LLVM. - auto align = layout.getABITypeAlign(type); + auto align = layout.getABITypeAlign(llvm_type); // TODO: Attach !tbaa.struct metadata indicating which portions of the // type we actually need to copy and which are padding. builder().CreateMemCpy(GetValue(dest_id), align, GetValue(source_id), align, - layout.getTypeAllocSize(type)); + layout.getTypeAllocSize(llvm_type)); } auto FunctionContext::Inserter::InsertHelper( @@ -316,6 +340,17 @@ auto FunctionContext::AddCallToCurrentFingerprint(SemIR::CheckIRId file_id, } } +auto FunctionContext::AddIntToCurrentFingerprint(uint64_t value) -> void { + if (!function_fingerprint_) { + return; + } + + // TODO: Instead just include the raw bytes of the integer? + RawStringOstream os; + os << value << "\n"; + current_fingerprint_.common_fingerprint.update(os.TakeStr()); +} + auto FunctionContext::AddTypeToCurrentFingerprint(llvm::Type* type) -> void { if (!function_fingerprint_ || !type) { return; diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index f6e9b057deb8..9c8c09d0ee34 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -5,6 +5,8 @@ #ifndef CARBON_TOOLCHAIN_LOWER_FUNCTION_CONTEXT_H_ #define CARBON_TOOLCHAIN_LOWER_FUNCTION_CONTEXT_H_ +#include + #include "common/map.h" #include "common/raw_string_ostream.h" #include "llvm/IR/IRBuilder.h" @@ -52,6 +54,39 @@ class FunctionContext { llvm::BLAKE3 specific_fingerprint; }; + // A type in a particular file. This is used when lowering a specific and we + // find a type that might be from the specific rather than the generic. By + // convention, this represents a type that has not yet been added to the + // specific fingerprint. + struct TypeInFile { + const SemIR::File* file; + SemIR::TypeId type_id; + }; + + // A value representation in a particular file. By convention, this represents + // a value representation whose kind has been added to the fingerprint but + // whose type has not. + struct ValueReprInFile { + const SemIR::File* file; + SemIR::ValueRepr repr; + + auto type() -> TypeInFile { + return {.file = file, .type_id = repr.type_id}; + } + }; + + // Information about a function's return type in a particular file. By + // convention, this represents a value whose initializing representation has + // been added to the fingerprint but whose type has not. + struct ReturnTypeInfoInFile { + const SemIR::File* file; + SemIR::ReturnTypeInfo info; + + auto type() -> TypeInFile { + return {.file = file, .type_id = info.type_id}; + } + }; + // Returns a basic block corresponding to the start of the given semantics // block, and enqueues it for emission. auto GetBlock(SemIR::InstBlockId block_id) -> llvm::BasicBlock*; @@ -70,7 +105,7 @@ class FunctionContext { // Returns a phi node corresponding to the block argument of the given basic // block. - auto GetBlockArg(SemIR::InstBlockId block_id, SemIR::TypeId type_id) + auto GetBlockArg(SemIR::InstBlockId block_id, TypeInFile type) -> llvm::PHINode*; // Returns a value for the given instruction. @@ -101,24 +136,33 @@ class FunctionContext { return file_context_->BuildFunctionTypeInfo(function, specific_id); } - // Returns a lowered type for the given type_id. - auto GetType(SemIR::TypeId type_id) -> llvm::Type* { - return file_context_->GetType(type_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* { + auto* llvm_type = GetFileContext(type.file).GetType(type.type_id); + AddTypeToCurrentFingerprint(llvm_type); + return llvm_type; } // Returns the type of the given instruction in the current specific. - auto GetTypeOfInstInSpecific(SemIR::InstId inst_id) -> llvm::Type* { - auto [type_file, type_id] = GetTypeIdOfInstInSpecific(inst_id); - auto* type = GetFileContext(type_file).GetType(type_id); - AddTypeToCurrentFingerprint(type); - return type; + auto GetTypeOfInst(SemIR::InstId inst_id) -> llvm::Type* { + return GetType(GetTypeIdOfInst(inst_id)); } // Returns the type of the given instruction in the current specific. - // TODO: Each caller of this should add information to the fingerprint - // indicating what information they used from the type. - auto GetTypeIdOfInstInSpecific(SemIR::InstId inst_id) - -> std::pair; + auto GetTypeIdOfInst(SemIR::InstId inst_id) -> TypeInFile; + + // Returns the value representation of the given type. This adds the kind of + // value representation, but not the underlying type, to the fingerprint. + auto GetValueRepr(TypeInFile type) -> ValueReprInFile; + + // Returns the initializing representation of the given type. This adds the + // kind of initializing representation to the fingerprint. + auto GetInitRepr(TypeInFile type) -> SemIR::InitRepr; + + // Returns the return type information for the given type. This adds the + // kind of initializing representation to the fingerprint. + auto GetReturnTypeInfo(TypeInFile type) -> ReturnTypeInfoInFile; // Returns a lowered value to use for a value of type `type`. auto GetTypeAsValue() -> llvm::Value* { @@ -161,7 +205,7 @@ class FunctionContext { // initialization of `dest_id` from that initializer. This is a no-op if the // initialization was performed in-place, and otherwise performs a store or a // copy. - auto FinishInit(SemIR::TypeId type_id, SemIR::InstId dest_id, + auto FinishInit(TypeInFile type, SemIR::InstId dest_id, SemIR::InstId source_id) -> void; // When fingerprinting for a specific, adds the call, found in the function @@ -171,6 +215,16 @@ class FunctionContext { SemIR::FunctionId function_id, SemIR::SpecificId specific_id) -> void; + // When fingerprinting for a specific, adds an integer. + auto AddIntToCurrentFingerprint(uint64_t value) -> void; + + // When fingerprinting for a specific, adds an enumerator value. + template + requires(std::is_enum_v) + auto AddEnumToCurrentFingerprint(T value) -> void { + AddIntToCurrentFingerprint(static_cast(value)); + } + // When fingerprinting for a specific, adds the type. auto AddTypeToCurrentFingerprint(llvm::Type* type) -> void; @@ -244,16 +298,16 @@ class FunctionContext { SemIR::InstId inst_id_ = SemIR::InstId::None; }; - // Emits a value copy for type `type_id` from `source_id` to `dest_id`. - // `source_id` must produce a value representation for `type_id`, and - // `dest_id` must be a pointer to a `type_id` object. - auto CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id, + // Emits a value copy for type `type` from `source_id` to `dest_id`. + // `source_id` must produce a value representation for `type`, and + // `dest_id` must be a pointer to a `type` object. + auto CopyValue(TypeInFile type, SemIR::InstId source_id, SemIR::InstId dest_id) -> void; - // Emits an object representation copy for type `type_id` from `source_id` to - // `dest_id`. `source_id` and `dest_id` must produce pointers to `type_id` + // Emits an object representation copy for type `type` from `source_id` to + // `dest_id`. `source_id` and `dest_id` must produce pointers to `type` // objects. - auto CopyObject(SemIR::TypeId type_id, SemIR::InstId source_id, + auto CopyObject(TypeInFile type, SemIR::InstId source_id, SemIR::InstId dest_id) -> void; // When fingerprinting for a specific, adds the global. diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 89a41e27e60b..7c211d5f4821 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -19,6 +19,17 @@ namespace Carbon::Lower { +// Returns whether this instruction names a namespace. +static auto IsNamespace(FunctionContext& context, SemIR::InstId inst_id) + -> bool { + // Note, we don't use context.GetTypeOfInst here. An instruction can't change + // from being a non-namespace in a generic to being a namespace in a specific, + // because namespace names are not first-class. + auto type_inst_id = context.sem_ir().types().GetInstId( + context.sem_ir().insts().Get(inst_id).type_id()); + return type_inst_id == SemIR::NamespaceType::TypeInstId; +} + auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::AddrOf inst) -> void { context.SetLocal(inst_id, context.GetValue(inst.lvalue_id)); @@ -27,21 +38,22 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ArrayIndex inst) -> void { auto* array_value = context.GetValue(inst.array_id); - auto* llvm_type = - context.GetType(context.sem_ir().insts().Get(inst.array_id).type_id()); + auto* llvm_type = context.GetTypeOfInst(inst.array_id); // The index in an `ArrayIndex` can be of any integer type, including // IntLiteral. If it is an IntLiteral, its value representation is empty, so // create a ConstantInt from its SemIR value directly. llvm::Value* index; - if (context.sem_ir().types().GetInstId( - context.sem_ir().insts().Get(inst.index_id).type_id()) == + auto index_type = context.GetTypeIdOfInst(inst.index_id); + if (index_type.file->types().GetInstId(index_type.type_id) == SemIR::IntLiteralType::TypeInstId) { auto value = context.sem_ir().insts().GetAs( context.sem_ir().constant_values().GetConstantInstId(inst.index_id)); - index = llvm::ConstantInt::get(context.llvm_context(), - context.sem_ir().ints().Get(value.int_id)); + const auto& apint_value = context.sem_ir().ints().Get(value.int_id); + context.AddIntToCurrentFingerprint(apint_value.getSExtValue()); + index = llvm::ConstantInt::get(context.llvm_context(), apint_value); } else { + context.AddIntToCurrentFingerprint(-1); index = context.GetValue(inst.index_id); } @@ -66,14 +78,13 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, SemIR::Assign inst) -> void { - auto storage_type_id = context.sem_ir().insts().Get(inst.lhs_id).type_id(); - context.FinishInit(storage_type_id, inst.lhs_id, inst.rhs_id); + context.FinishInit(context.GetTypeIdOfInst(inst.lhs_id), inst.lhs_id, + inst.rhs_id); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::BindAlias inst) -> void { - auto type_inst_id = context.sem_ir().types().GetInstId(inst.type_id); - if (type_inst_id == SemIR::NamespaceType::TypeInstId) { + if (IsNamespace(context, inst_id)) { return; } @@ -82,8 +93,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ExportDecl inst) -> void { - auto type_inst_id = context.sem_ir().types().GetInstId(inst.type_id); - if (type_inst_id == SemIR::NamespaceType::TypeInstId) { + if (IsNamespace(context, inst_id)) { return; } @@ -102,7 +112,9 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::BlockArg inst) -> void { - context.SetLocal(inst_id, context.GetBlockArg(inst.block_id, inst.type_id)); + context.SetLocal( + inst_id, + context.GetBlockArg(inst.block_id, context.GetTypeIdOfInst(inst_id))); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, @@ -138,8 +150,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, SemIR::BranchWithArg inst) -> void { llvm::Value* arg = context.GetValue(inst.arg_id); - SemIR::TypeId arg_type_id = - context.sem_ir().insts().Get(inst.arg_id).type_id(); + auto arg_type = context.GetTypeIdOfInst(inst.arg_id); // Opportunistically avoid creating a BasicBlock that contains just a branch. // We only do this for a block that we know will only have a single @@ -157,7 +168,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, context.builder().CreateBr(context.GetBlock(inst.target_id)); } - context.GetBlockArg(inst.target_id, arg_type_id) + context.GetBlockArg(inst.target_id, arg_type) ->addIncoming(arg, phi_predecessor); context.builder().ClearInsertionPoint(); } @@ -179,8 +190,8 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, SemIR::InitializeFrom inst) -> void { - auto storage_type_id = context.sem_ir().insts().Get(inst.dest_id).type_id(); - context.FinishInit(storage_type_id, inst.dest_id, inst.src_id); + context.FinishInit(context.GetTypeIdOfInst(inst.dest_id), inst.dest_id, + inst.src_id); } auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, @@ -190,8 +201,7 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::NameRef inst) -> void { - auto type_inst_id = context.sem_ir().types().GetInstId(inst.type_id); - if (type_inst_id == SemIR::NamespaceType::TypeInstId) { + if (IsNamespace(context, inst_id)) { return; } @@ -222,7 +232,7 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ReturnSlot inst) -> void { - if (SemIR::InitRepr::ForType(context.sem_ir(), inst.type_id).kind == + if (context.GetInitRepr(context.GetTypeIdOfInst(inst_id)).kind == SemIR::InitRepr::InPlace) { context.SetLocal(inst_id, context.GetValue(inst.storage_id)); } @@ -235,14 +245,14 @@ auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, SemIR::ReturnExpr inst) -> void { - auto result_type_id = context.sem_ir().insts().Get(inst.expr_id).type_id(); - switch (SemIR::InitRepr::ForType(context.sem_ir(), result_type_id).kind) { + auto result_type = context.GetTypeIdOfInst(inst.expr_id); + switch (context.GetInitRepr(result_type).kind) { case SemIR::InitRepr::None: // Nothing to return. context.builder().CreateRetVoid(); return; case SemIR::InitRepr::InPlace: - context.FinishInit(result_type_id, inst.dest_id, inst.expr_id); + context.FinishInit(result_type, inst.dest_id, inst.expr_id); context.builder().CreateRetVoid(); return; case SemIR::InitRepr::ByCopy: @@ -251,7 +261,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, return; case SemIR::InitRepr::Incomplete: CARBON_FATAL("Lowering return of incomplete type {0}", - context.sem_ir().types().GetAsInst(result_type_id)); + result_type.file->types().GetAsInst(result_type.type_id)); } } @@ -286,7 +296,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::VarStorage /* inst */) -> void { - auto* type = context.GetTypeOfInstInSpecific(inst_id); + auto* type = context.GetTypeOfInst(inst_id); // Position the first alloca right before the start of the executable code in // the function. diff --git a/toolchain/lower/handle_aggregates.cpp b/toolchain/lower/handle_aggregates.cpp index 649f2b5f60bb..17773b7f5356 100644 --- a/toolchain/lower/handle_aggregates.cpp +++ b/toolchain/lower/handle_aggregates.cpp @@ -20,15 +20,20 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, // No action to perform. } +static auto GetPointeeType(FunctionContext::TypeInFile type) + -> FunctionContext::TypeInFile { + return {.file = type.file, + .type_id = type.file->GetPointeeType(type.type_id)}; +} + // Extracts an element of an aggregate, such as a struct, tuple, or class, by // index. Depending on the expression category and value representation of the // aggregate input, this will either produce a value or a reference. static auto GetAggregateElement(FunctionContext& context, SemIR::InstId aggr_inst_id, SemIR::ElementIndex idx, - SemIR::TypeId result_type_id, llvm::Twine name) + SemIR::InstId result_inst_id, llvm::Twine name) -> llvm::Value* { - auto aggr_inst = context.sem_ir().insts().Get(aggr_inst_id); auto* aggr_value = context.GetValue(aggr_inst_id); switch (SemIR::GetExprCategory(context.sem_ir(), aggr_inst_id)) { @@ -39,11 +44,12 @@ static auto GetAggregateElement(FunctionContext& context, CARBON_FATAL("Unexpected expression category for aggregate access"); case SemIR::ExprCategory::Value: { - auto value_rep = - SemIR::ValueRepr::ForType(context.sem_ir(), aggr_inst.type_id()); - CARBON_CHECK(value_rep.aggregate_kind != SemIR::ValueRepr::NotAggregate, - "aggregate type should have aggregate value representation"); - switch (value_rep.kind) { + auto aggr_type = context.GetTypeIdOfInst(aggr_inst_id); + auto value_repr = context.GetValueRepr(aggr_type); + CARBON_CHECK( + value_repr.repr.aggregate_kind != SemIR::ValueRepr::NotAggregate, + "aggregate type should have aggregate value representation"); + switch (value_repr.repr.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL("Lowering access to incomplete aggregate type"); case SemIR::ValueRepr::None: @@ -55,24 +61,21 @@ static auto GetAggregateElement(FunctionContext& context, case SemIR::ValueRepr::Pointer: { // The value representation is a pointer to an aggregate that we want // to index into. - auto pointee_type_id = - context.sem_ir().GetPointeeType(value_rep.type_id); - auto* value_type = context.GetType(pointee_type_id); + auto* value_type = context.GetType(GetPointeeType(value_repr.type())); auto* elem_ptr = context.builder().CreateStructGEP( value_type, aggr_value, idx.index, name); - if (!value_rep.elements_are_values()) { + if (!value_repr.repr.elements_are_values()) { // `elem_ptr` points to an object representation, which is our // result. return elem_ptr; } // `elem_ptr` points to a value representation. Load it. - auto result_value_type_id = - SemIR::ValueRepr::ForType(context.sem_ir(), result_type_id) - .type_id; + auto result_type = context.GetTypeIdOfInst(result_inst_id); + auto result_value_type = context.GetValueRepr(result_type).type(); return context.builder().CreateLoad( - context.GetType(result_value_type_id), elem_ptr, name + ".load"); + context.GetType(result_value_type), elem_ptr, name + ".load"); } case SemIR::ValueRepr::Custom: CARBON_FATAL( @@ -83,46 +86,48 @@ static auto GetAggregateElement(FunctionContext& context, case SemIR::ExprCategory::DurableRef: case SemIR::ExprCategory::EphemeralRef: { // Just locate the aggregate element. - auto* aggr_type = context.GetType(aggr_inst.type_id()); + auto* aggr_type = context.GetTypeOfInst(aggr_inst_id); return context.builder().CreateStructGEP(aggr_type, aggr_value, idx.index, name); } } } -static auto GetStructFieldName(FunctionContext& context, - SemIR::TypeId struct_type_id, +static auto GetStructFieldName(FunctionContext::TypeInFile struct_type, SemIR::ElementIndex index) -> llvm::StringRef { - auto struct_type = - context.sem_ir().types().GetAs(struct_type_id); + auto struct_type_inst = + struct_type.file->types().GetAs(struct_type.type_id); auto fields = - context.sem_ir().struct_type_fields().Get(struct_type.fields_id); - return context.sem_ir().names().GetIRBaseName(fields[index.index].name_id); + struct_type.file->struct_type_fields().Get(struct_type_inst.fields_id); + // We intentionally don't add this to the fingerprint because it's only used + // as an instruction name, and so doesn't affect the semantics of the IR. + return struct_type.file->names().GetIRBaseName(fields[index.index].name_id); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ClassElementAccess inst) -> void { // Find the class that we're performing access into. - auto class_type_id = context.sem_ir().insts().Get(inst.base_id).type_id(); - SemIR::TypeId object_repr_id = - context.sem_ir().types().GetObjectRepr(class_type_id); + auto class_type = context.GetTypeIdOfInst(inst.base_id); + auto object_repr = FunctionContext::TypeInFile{ + .file = class_type.file, + .type_id = class_type.file->types().GetObjectRepr(class_type.type_id)}; // Translate the class field access into a struct access on the object // representation. - context.SetLocal( - inst_id, GetAggregateElement( - context, inst.base_id, inst.index, inst.type_id, - GetStructFieldName(context, object_repr_id, inst.index))); + context.SetLocal(inst_id, GetAggregateElement( + context, inst.base_id, inst.index, inst_id, + GetStructFieldName(object_repr, inst.index))); } static auto EmitAggregateInitializer(FunctionContext& context, - SemIR::TypeId type_id, + SemIR::InstId init_inst_id, SemIR::InstBlockId refs_id, llvm::Twine name) -> llvm::Value* { - auto* llvm_type = context.GetType(type_id); + auto type = context.GetTypeIdOfInst(init_inst_id); + auto* llvm_type = context.GetType(type); auto refs = context.sem_ir().inst_blocks().Get(refs_id); - switch (SemIR::InitRepr::ForType(context.sem_ir(), type_id).kind) { + switch (context.GetInitRepr(type).kind) { case SemIR::InitRepr::None: { // TODO: Add a helper to poison a value slot. return llvm::PoisonValue::get(llvm_type); @@ -146,9 +151,8 @@ static auto EmitAggregateInitializer(FunctionContext& context, auto dest_id = SemIR::FindReturnSlotArgForInitializer(context.sem_ir(), ref_id); auto src_id = ref_id; - auto storage_type_id = - context.sem_ir().insts().Get(dest_id).type_id(); - context.FinishInit(storage_type_id, dest_id, src_id); + auto storage_type = context.GetTypeIdOfInst(dest_id); + context.FinishInit(storage_type, dest_id, src_id); } } // TODO: Add a helper to poison a value slot. @@ -169,24 +173,23 @@ static auto EmitAggregateInitializer(FunctionContext& context, case SemIR::InitRepr::Incomplete: CARBON_FATAL("Lowering aggregate initialization of incomplete type {0}", - context.sem_ir().types().GetAsInst(type_id)); + type.file->types().GetAsInst(type.type_id)); } } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ClassInit inst) -> void { - context.SetLocal( - inst_id, EmitAggregateInitializer(context, inst.type_id, inst.elements_id, - "class.init")); + context.SetLocal(inst_id, + EmitAggregateInitializer(context, inst_id, inst.elements_id, + "class.init")); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::StructAccess inst) -> void { - auto struct_type_id = context.sem_ir().insts().Get(inst.struct_id).type_id(); - context.SetLocal( - inst_id, GetAggregateElement( - context, inst.struct_id, inst.index, inst.type_id, - GetStructFieldName(context, struct_type_id, inst.index))); + auto struct_type = context.GetTypeIdOfInst(inst.struct_id); + context.SetLocal(inst_id, GetAggregateElement( + context, inst.struct_id, inst.index, inst_id, + GetStructFieldName(struct_type, inst.index))); } auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, @@ -198,16 +201,18 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, // Emits the value representation for a struct or tuple whose elements are the // contents of `refs_id`. static auto EmitAggregateValueRepr(FunctionContext& context, - SemIR::TypeId type_id, + SemIR::InstId value_inst_id, SemIR::InstBlockId refs_id) -> llvm::Value* { - auto value_rep = SemIR::ValueRepr::ForType(context.sem_ir(), type_id); - switch (value_rep.kind) { + auto type = context.GetTypeIdOfInst(value_inst_id); + auto value_repr = context.GetValueRepr(type); + auto value_type = value_repr.type(); + switch (value_repr.repr.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL("Incomplete aggregate type in lowering"); case SemIR::ValueRepr::None: // TODO: Add a helper to get a "no value representation" value. - return llvm::PoisonValue::get(context.GetType(value_rep.type_id)); + return llvm::PoisonValue::get(context.GetType(value_type)); case SemIR::ValueRepr::Copy: { auto refs = context.sem_ir().inst_blocks().Get(refs_id); @@ -217,13 +222,12 @@ static auto EmitAggregateValueRepr(FunctionContext& context, // TODO: Remove the LLVM StructType wrapper in this case, so we don't // need this `insert_value` wrapping. return context.builder().CreateInsertValue( - llvm::PoisonValue::get(context.GetType(value_rep.type_id)), + llvm::PoisonValue::get(context.GetType(value_type)), context.GetValue(refs[0]), {0}); } case SemIR::ValueRepr::Pointer: { - auto pointee_type_id = context.sem_ir().GetPointeeType(value_rep.type_id); - auto* llvm_value_rep_type = context.GetType(pointee_type_id); + auto* llvm_value_rep_type = context.GetType(GetPointeeType(value_type)); // Write the value representation to a local alloca so we can produce a // pointer to it as the value representation of the struct or tuple. @@ -244,28 +248,30 @@ static auto EmitAggregateValueRepr(FunctionContext& context, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::StructInit inst) -> void { - context.SetLocal( - inst_id, EmitAggregateInitializer(context, inst.type_id, inst.elements_id, - "struct.init")); + context.SetLocal(inst_id, + EmitAggregateInitializer(context, inst_id, inst.elements_id, + "struct.init")); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::StructValue inst) -> void { - if (auto fn_type = context.sem_ir().types().TryGetAs( - inst.type_id)) { - context.SetLocal(inst_id, context.GetFunction(fn_type->function_id)); + auto type = context.GetTypeIdOfInst(inst_id); + if (auto fn_type = + type.file->types().TryGetAs(type.type_id)) { + context.SetLocal(inst_id, context.GetFileContext(type.file).GetFunction( + fn_type->function_id)); return; } - context.SetLocal( - inst_id, EmitAggregateValueRepr(context, inst.type_id, inst.elements_id)); + context.SetLocal(inst_id, + EmitAggregateValueRepr(context, inst_id, inst.elements_id)); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::TupleAccess inst) -> void { - context.SetLocal(inst_id, - GetAggregateElement(context, inst.tuple_id, inst.index, - inst.type_id, "tuple.elem")); + context.SetLocal( + inst_id, GetAggregateElement(context, inst.tuple_id, inst.index, inst_id, + "tuple.elem")); } auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, @@ -276,15 +282,15 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::TupleInit inst) -> void { - context.SetLocal( - inst_id, EmitAggregateInitializer(context, inst.type_id, inst.elements_id, - "tuple.init")); + context.SetLocal(inst_id, + EmitAggregateInitializer(context, inst_id, inst.elements_id, + "tuple.init")); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::TupleValue inst) -> void { - context.SetLocal( - inst_id, EmitAggregateValueRepr(context, inst.type_id, inst.elements_id)); + context.SetLocal(inst_id, + EmitAggregateValueRepr(context, inst_id, inst.elements_id)); } } // namespace Carbon::Lower diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 3860c03c447f..29f1c210fd55 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -64,7 +64,7 @@ static auto GetBuiltinFCmpPredicate(SemIR::BuiltinFunctionKind builtin_kind) // Returns whether the specified instruction has a signed integer type. static auto IsSignedInt(FunctionContext& context, SemIR::InstId int_id) -> bool { - auto [file, type_id] = context.GetTypeIdOfInstInSpecific(int_id); + auto [file, type_id] = context.GetTypeIdOfInst(int_id); return file->types().IsSignedInt(type_id); } @@ -226,8 +226,7 @@ static auto CreateBinaryOperatorForBuiltin( // arithmetic or logical shift. auto lhs_id = context.sem_ir().inst_blocks().Get( context.sem_ir().insts().GetAs(inst_id).args_id)[0]; - auto [lhs_type_file, lhs_type_id] = - context.GetTypeIdOfInstInSpecific(lhs_id); + auto [lhs_type_file, lhs_type_id] = context.GetTypeIdOfInst(lhs_id); if (builtin_kind == SemIR::BuiltinFunctionKind::IntRightShiftAssign) { lhs_type_id = lhs_type_file->GetPointeeType(lhs_type_id); } @@ -266,11 +265,8 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, auto putchar = context.llvm_module().getOrInsertFunction( "putchar", i32_type, i32_type); auto* result = context.builder().CreateCall(putchar, {arg_value}); - context.SetLocal( - inst_id, - context.builder().CreateSExtOrTrunc( - result, context.GetType( - context.sem_ir().insts().Get(inst_id).type_id()))); + context.SetLocal(inst_id, context.builder().CreateSExtOrTrunc( + result, context.GetTypeOfInst(inst_id))); return; } @@ -295,11 +291,8 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, auto getchar = context.llvm_module().getOrInsertFunction("getchar", i32_type); auto* result = context.builder().CreateCall(getchar, {}); - context.SetLocal( - inst_id, - context.builder().CreateSExtOrTrunc( - result, context.GetType( - context.sem_ir().insts().Get(inst_id).type_id()))); + context.SetLocal(inst_id, context.builder().CreateSExtOrTrunc( + result, context.GetTypeOfInst(inst_id))); return; } @@ -317,12 +310,10 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, return; case SemIR::BuiltinFunctionKind::IntConvert: { - context.SetLocal( - inst_id, - CreateExtOrTrunc( - context, context.GetValue(arg_ids[0]), - context.GetType(context.sem_ir().insts().Get(inst_id).type_id()), - IsSignedInt(context, arg_ids[0]))); + context.SetLocal(inst_id, + CreateExtOrTrunc(context, context.GetValue(arg_ids[0]), + context.GetTypeOfInst(inst_id), + IsSignedInt(context, arg_ids[0]))); return; } @@ -392,8 +383,7 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::IntLeftShiftAssign: case SemIR::BuiltinFunctionKind::IntRightShiftAssign: { auto* lhs_ptr = context.GetValue(arg_ids[0]); - auto [lhs_type_file, lhs_type_id] = - context.GetTypeIdOfInstInSpecific(arg_ids[0]); + auto [lhs_type_file, lhs_type_id] = context.GetTypeIdOfInst(arg_ids[0]); auto pointee_type_id = lhs_type_file->GetPointeeType(lhs_type_id); // TODO: Factor out the code to create loads and stores, and include alias // and alignment information. @@ -405,8 +395,8 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, context.GetValue(arg_ids[1])); context.builder().CreateStore(result, lhs_ptr); // TODO: Add a helper to get a "no value representation" value. - context.SetLocal(inst_id, llvm::PoisonValue::get( - context.GetTypeOfInstInSpecific(inst_id))); + context.SetLocal(inst_id, + llvm::PoisonValue::get(context.GetTypeOfInst(inst_id))); return; } case SemIR::BuiltinFunctionKind::IntEq: @@ -520,20 +510,15 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, std::vector args; - auto [inst_type_file, inst_type_id] = - context.GetTypeIdOfInstInSpecific(inst_id); - - if (SemIR::ReturnTypeInfo::ForType(*inst_type_file, inst_type_id) - .has_return_slot()) { + auto inst_type = context.GetTypeIdOfInst(inst_id); + if (context.GetReturnTypeInfo(inst_type).info.has_return_slot()) { args.push_back(context.GetValue(arg_ids.back())); arg_ids = arg_ids.drop_back(); } for (auto arg_id : arg_ids) { - auto [arg_type_file, arg_type_id] = - context.GetTypeIdOfInstInSpecific(arg_id); - if (SemIR::ValueRepr::ForType(*arg_type_file, arg_type_id).kind != - SemIR::ValueRepr::None) { + auto arg_type = context.GetTypeIdOfInst(arg_id); + if (context.GetValueRepr(arg_type).repr.kind != SemIR::ValueRepr::None) { args.push_back(context.GetValue(arg_id)); } } diff --git a/toolchain/lower/handle_expr_category.cpp b/toolchain/lower/handle_expr_category.cpp index 1c1ce66bd5fb..a36cc896ca94 100644 --- a/toolchain/lower/handle_expr_category.cpp +++ b/toolchain/lower/handle_expr_category.cpp @@ -10,8 +10,8 @@ namespace Carbon::Lower { auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::BindValue inst) -> void { - switch (auto rep = SemIR::ValueRepr::ForType(context.sem_ir(), inst.type_id); - rep.kind) { + auto inst_type = context.GetTypeIdOfInst(inst_id); + switch (context.GetValueRepr(inst_type).repr.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL( "Value binding for type with incomplete value representation"); @@ -20,12 +20,10 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, // propagate. // TODO: Remove this now the StubRefs are gone. context.SetLocal(inst_id, - llvm::PoisonValue::get(context.GetType(inst.type_id))); + llvm::PoisonValue::get(context.GetType(inst_type))); break; case SemIR::ValueRepr::Copy: { - auto* type = context.GetType(SemIR::GetTypeOfInstInSpecific( - context.sem_ir(), context.specific_id(), inst_id)); - context.AddTypeToCurrentFingerprint(type); + auto* type = context.GetType(inst_type); context.SetLocal(inst_id, context.builder().CreateLoad( type, context.GetValue(inst.value_id))); } break; @@ -39,15 +37,14 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::Temporary inst) -> void { - context.FinishInit(inst.type_id, inst.storage_id, inst.init_id); + context.FinishInit(context.GetTypeIdOfInst(inst_id), inst.storage_id, + inst.init_id); context.SetLocal(inst_id, context.GetValue(inst.storage_id)); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::TemporaryStorage /*inst*/) -> void { - auto* type = context.GetType(SemIR::GetTypeOfInstInSpecific( - context.sem_ir(), context.specific_id(), inst_id)); - context.AddTypeToCurrentFingerprint(type); + auto* type = context.GetTypeOfInst(inst_id); context.SetLocal(inst_id, context.builder().CreateAlloca(type, nullptr, "temp")); } @@ -56,8 +53,9 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ValueAsRef inst) -> void { CARBON_CHECK(SemIR::GetExprCategory(context.sem_ir(), inst.value_id) == SemIR::ExprCategory::Value); - CARBON_CHECK(SemIR::ValueRepr::ForType(context.sem_ir(), inst.type_id).kind == - SemIR::ValueRepr::Pointer); + auto inst_type = context.GetTypeIdOfInst(inst_id); + auto value_repr = context.GetValueRepr(inst_type); + CARBON_CHECK(value_repr.repr.kind == SemIR::ValueRepr::Pointer); context.SetLocal(inst_id, context.GetValue(inst.value_id)); } @@ -65,10 +63,11 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ValueOfInitializer inst) -> void { CARBON_CHECK(SemIR::GetExprCategory(context.sem_ir(), inst.init_id) == SemIR::ExprCategory::Initializing); - CARBON_CHECK(SemIR::ValueRepr::ForType(context.sem_ir(), inst.type_id).kind == - SemIR::ValueRepr::Copy); - CARBON_CHECK(SemIR::InitRepr::ForType(context.sem_ir(), inst.type_id).kind == - SemIR::InitRepr::ByCopy); + auto inst_type = context.GetTypeIdOfInst(inst_id); + auto value_repr = context.GetValueRepr(inst_type); + auto init_repr = context.GetInitRepr(inst_type); + CARBON_CHECK(value_repr.repr.kind == SemIR::ValueRepr::Copy); + CARBON_CHECK(init_repr.kind == SemIR::InitRepr::ByCopy); context.SetLocal(inst_id, context.GetValue(inst.init_id)); } diff --git a/toolchain/lower/testdata/class/generic.carbon b/toolchain/lower/testdata/class/generic.carbon index 85565605df5e..dc787f78d687 100644 --- a/toolchain/lower/testdata/class/generic.carbon +++ b/toolchain/lower/testdata/class/generic.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -41,6 +41,77 @@ fn CreateAdapter() -> Classes.Adapter(i32) { return CreateDerived() as Classes.Adapter(i32); } +// --- create_generic.carbon + +library "[[@TEST_NAME]]"; + +class A(T:! type) { + var x: T; + var y: T; +} + +fn Make[T:! type](x: T, y: T) -> A(T) { + // TODO: This should probably require a `Copy` constraint on `T`. + // For now we avoid calling it in cases that would attempt to + // copy a non-copyable type, as that would probably crash. + return {.x = x, .y = y}; +} + +fn Ints() -> A(i32) { + return Make(1 as i32, 2 as i32); +} + +fn Empty() -> A(()) { + return Make((), ()); +} + +fn Tuples() -> A((i32, i32)) { + let x: (i32, i32) = (1, 2); + // TODO: This fails to initialize the `A` object's fields. + // This happens because we generate SemIR assuming that the dependent + // type will have a copy value representation, and will need changes + // in SemIR for us to properly lower it. + return Make(x, x); +} + +// --- access.carbon + +library "[[@TEST_NAME]]"; + +class C(T:! type) { + fn GetBool[self: Self]() -> bool { + return self.v; + } + // TODO: This should probably require a `Copy` constraint on `T`. + // For now we avoid calling it in cases that would attempt to + // copy a non-copyable type, as that would probably crash. + fn GetT[self: Self]() -> T { + return self.w; + } + var v: bool; + var w: T; +} + +fn AccessBool() -> bool { + var c: C(i32) = {.v = true, .w = 0}; + return c.GetBool(); +} + +fn AccessInt() -> i32 { + var c: C(i32) = {.v = true, .w = 0}; + return c.GetT(); +} + +fn AccessEmpty() -> () { + var c: C(()) = {.v = true, .w = ()}; + return c.GetT(); +} + +fn AccessTuple() -> (i32, i32, i32) { + var c: C((i32, i32, i32)) = {.v = true, .w = (1, 2, 3)}; + return c.GetT(); +} + // CHECK:STDOUT: ; ModuleID = 'classes.carbon' // CHECK:STDOUT: source_filename = "classes.carbon" // CHECK:STDOUT: @@ -92,3 +163,212 @@ fn CreateAdapter() -> Classes.Adapter(i32) { // CHECK:STDOUT: !10 = distinct !DISubprogram(name: "CreateAdapter", linkageName: "_CCreateAdapter.Create", scope: null, file: !3, line: 10, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !11 = !DILocation(line: 11, column: 10, scope: !10) // CHECK:STDOUT: !12 = !DILocation(line: 11, column: 3, scope: !10) +// CHECK:STDOUT: ; ModuleID = 'create_generic.carbon' +// CHECK:STDOUT: source_filename = "create_generic.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @tuple.loc25_28.6 = internal constant { i32, i32 } { i32 1, i32 2 } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CInts.Main(ptr sret({ i32, i32 }) %return) !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CMake.Main.b88d1103f417c6d4(ptr %return, i32 1, i32 2), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CEmpty.Main(ptr sret({ {}, {} }) %return) !dbg !9 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CMake.Main.e43630e9a6c38c3f(ptr %return), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CTuples.Main(ptr sret({ { i32, i32 }, { i32, i32 } }) %return) !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CMake.Main.29d34654e802e24e(ptr %return, ptr @tuple.loc25_28.6, ptr @tuple.loc25_28.6), !dbg !13 +// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CMake.Main.b88d1103f417c6d4(ptr sret({ i32, i32 }) %return, i32 %x, i32 %y) !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc13_25.2.x = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !16 +// CHECK:STDOUT: store i32 %x, ptr %.loc13_25.2.x, align 4, !dbg !16 +// CHECK:STDOUT: %.loc13_25.4.y = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !16 +// CHECK:STDOUT: store i32 %y, ptr %.loc13_25.4.y, align 4, !dbg !16 +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CMake.Main.e43630e9a6c38c3f(ptr sret({ {}, {} }) %return) !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc13_25.2.x = getelementptr inbounds nuw { {}, {} }, ptr %return, i32 0, i32 0, !dbg !19 +// CHECK:STDOUT: %.loc13_25.4.y = getelementptr inbounds nuw { {}, {} }, ptr %return, i32 0, i32 1, !dbg !19 +// CHECK:STDOUT: ret void, !dbg !20 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CMake.Main.29d34654e802e24e(ptr sret({ { i32, i32 }, { i32, i32 } }) %return, ptr %x, ptr %y) !dbg !21 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc13_25.2.x = getelementptr inbounds nuw { { i32, i32 }, { i32, i32 } }, ptr %return, i32 0, i32 0, !dbg !22 +// CHECK:STDOUT: %.loc13_25.4.y = getelementptr inbounds nuw { { i32, i32 }, { i32, i32 } }, ptr %return, i32 0, i32 1, !dbg !22 +// CHECK:STDOUT: ret void, !dbg !23 +// CHECK:STDOUT: } +// 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, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "create_generic.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Ints", linkageName: "_CInts.Main", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 17, column: 10, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 17, column: 3, scope: !4) +// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "Empty", linkageName: "_CEmpty.Main", scope: null, file: !3, line: 20, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !10 = !DILocation(line: 21, column: 10, scope: !9) +// CHECK:STDOUT: !11 = !DILocation(line: 21, column: 3, scope: !9) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "Tuples", linkageName: "_CTuples.Main", scope: null, file: !3, line: 24, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DILocation(line: 30, column: 10, scope: !12) +// CHECK:STDOUT: !14 = !DILocation(line: 30, column: 3, scope: !12) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "Make", linkageName: "_CMake.Main.b88d1103f417c6d4", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !16 = !DILocation(line: 13, column: 10, scope: !15) +// CHECK:STDOUT: !17 = !DILocation(line: 13, column: 3, scope: !15) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "Make", linkageName: "_CMake.Main.e43630e9a6c38c3f", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !19 = !DILocation(line: 13, column: 10, scope: !18) +// CHECK:STDOUT: !20 = !DILocation(line: 13, column: 3, scope: !18) +// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "Make", linkageName: "_CMake.Main.29d34654e802e24e", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !22 = !DILocation(line: 13, column: 10, scope: !21) +// CHECK:STDOUT: !23 = !DILocation(line: 13, column: 3, scope: !21) +// CHECK:STDOUT: ; ModuleID = 'access.carbon' +// CHECK:STDOUT: source_filename = "access.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @C.val.98f.loc19_3 = internal constant { i1, i32 } { i1 true, i32 0 } +// CHECK:STDOUT: @C.val.dcc.loc29_3 = internal constant { i1, {} } { i1 true, {} zeroinitializer } +// CHECK:STDOUT: @C.val.08f.loc34_3 = internal constant { i1, { i32, i32, i32 } } { i1 true, { i32, i32, i32 } { i32 1, i32 2, i32 3 } } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CAccessBool.Main() !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %c.var = alloca { i1, i32 }, align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %c.var), !dbg !7 +// CHECK:STDOUT: %.loc19_37.2.v = getelementptr inbounds nuw { i1, i32 }, ptr %c.var, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: %.loc19_37.5.w = getelementptr inbounds nuw { i1, i32 }, ptr %c.var, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %c.var, ptr align 4 @C.val.98f.loc19_3, i64 8, i1 false), !dbg !7 +// CHECK:STDOUT: %GetBool.call = call i1 @_CGetBool.C.Main.b88d1103f417c6d4(ptr %c.var), !dbg !9 +// CHECK:STDOUT: ret i1 %GetBool.call, !dbg !10 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CAccessInt.Main() !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %c.var = alloca { i1, i32 }, align 8, !dbg !12 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %c.var), !dbg !12 +// CHECK:STDOUT: %.loc24_37.2.v = getelementptr inbounds nuw { i1, i32 }, ptr %c.var, i32 0, i32 0, !dbg !13 +// CHECK:STDOUT: %.loc24_37.5.w = getelementptr inbounds nuw { i1, i32 }, ptr %c.var, i32 0, i32 1, !dbg !13 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %c.var, ptr align 4 @C.val.98f.loc19_3, i64 8, i1 false), !dbg !12 +// CHECK:STDOUT: %GetT.call = call i32 @_CGetT.C.Main.b88d1103f417c6d4(ptr %c.var), !dbg !14 +// CHECK:STDOUT: ret i32 %GetT.call, !dbg !15 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CAccessEmpty.Main() !dbg !16 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %c.var = alloca { i1, {} }, align 8, !dbg !17 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 1, ptr %c.var), !dbg !17 +// CHECK:STDOUT: %.loc29_37.2.v = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 0, !dbg !18 +// CHECK:STDOUT: %.loc29_37.4.w = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 1, !dbg !18 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @C.val.dcc.loc29_3, i64 1, i1 false), !dbg !17 +// CHECK:STDOUT: call void @_CGetT.C.Main.e43630e9a6c38c3f(ptr %c.var), !dbg !19 +// CHECK:STDOUT: %.loc30_17.1.temp = alloca {}, align 8, !dbg !19 +// CHECK:STDOUT: ret void, !dbg !20 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CAccessTuple.Main(ptr sret({ i32, i32, i32 }) %return) !dbg !21 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %c.var = alloca { i1, { i32, i32, i32 } }, align 8, !dbg !22 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 16, ptr %c.var), !dbg !22 +// CHECK:STDOUT: %.loc34_57.2.v = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %c.var, i32 0, i32 0, !dbg !23 +// CHECK:STDOUT: %.loc34_57.4.w = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %c.var, i32 0, i32 1, !dbg !23 +// CHECK:STDOUT: %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc34_57.4.w, i32 0, i32 0, !dbg !24 +// CHECK:STDOUT: %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc34_57.4.w, i32 0, i32 1, !dbg !24 +// CHECK:STDOUT: %tuple.elem2.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %.loc34_57.4.w, i32 0, i32 2, !dbg !24 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %c.var, ptr align 4 @C.val.08f.loc34_3, i64 16, i1 false), !dbg !22 +// CHECK:STDOUT: call void @_CGetT.C.Main.69a44aeea2e5c3f0(ptr %return, ptr %c.var), !dbg !25 +// CHECK:STDOUT: ret void, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr i1 @_CGetBool.C.Main.b88d1103f417c6d4(ptr %self) !dbg !27 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc6_16.1.v = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !28 +// CHECK:STDOUT: %.loc6_16.2 = load i1, ptr %.loc6_16.1.v, align 1, !dbg !28 +// CHECK:STDOUT: ret i1 %.loc6_16.2, !dbg !29 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr i32 @_CGetT.C.Main.b88d1103f417c6d4(ptr %self) !dbg !30 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc12_16.1.w = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 1, !dbg !31 +// CHECK:STDOUT: %.loc12_16.2 = load i32, ptr %.loc12_16.1.w, align 4, !dbg !31 +// CHECK:STDOUT: ret i32 %.loc12_16.2, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CGetT.C.Main.e43630e9a6c38c3f(ptr %self) !dbg !33 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc12_16.1.w = getelementptr inbounds nuw { i1, {} }, ptr %self, i32 0, i32 1, !dbg !34 +// CHECK:STDOUT: ret void, !dbg !35 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CGetT.C.Main.69a44aeea2e5c3f0(ptr sret({ i32, i32, i32 }) %return, ptr %self) !dbg !36 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc12_16.1.w = getelementptr inbounds nuw { i1, { i32, i32, i32 } }, ptr %self, i32 0, i32 1, !dbg !37 +// CHECK:STDOUT: ret void, !dbg !38 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 3, 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #1 = { nocallback nofree 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, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "access.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "AccessBool", linkageName: "_CAccessBool.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 19, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 19, column: 19, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 20, column: 10, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 20, column: 3, scope: !4) +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "AccessInt", linkageName: "_CAccessInt.Main", scope: null, file: !3, line: 23, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !12 = !DILocation(line: 24, column: 3, scope: !11) +// CHECK:STDOUT: !13 = !DILocation(line: 24, column: 19, scope: !11) +// CHECK:STDOUT: !14 = !DILocation(line: 25, column: 10, scope: !11) +// CHECK:STDOUT: !15 = !DILocation(line: 25, column: 3, scope: !11) +// CHECK:STDOUT: !16 = distinct !DISubprogram(name: "AccessEmpty", linkageName: "_CAccessEmpty.Main", scope: null, file: !3, line: 28, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !17 = !DILocation(line: 29, column: 3, scope: !16) +// CHECK:STDOUT: !18 = !DILocation(line: 29, column: 18, scope: !16) +// CHECK:STDOUT: !19 = !DILocation(line: 30, column: 10, scope: !16) +// CHECK:STDOUT: !20 = !DILocation(line: 30, column: 3, scope: !16) +// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "AccessTuple", linkageName: "_CAccessTuple.Main", scope: null, file: !3, line: 33, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !22 = !DILocation(line: 34, column: 3, scope: !21) +// CHECK:STDOUT: !23 = !DILocation(line: 34, column: 31, scope: !21) +// CHECK:STDOUT: !24 = !DILocation(line: 34, column: 48, scope: !21) +// CHECK:STDOUT: !25 = !DILocation(line: 35, column: 10, scope: !21) +// CHECK:STDOUT: !26 = !DILocation(line: 35, column: 3, scope: !21) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "GetBool", linkageName: "_CGetBool.C.Main.b88d1103f417c6d4", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !28 = !DILocation(line: 6, column: 12, scope: !27) +// CHECK:STDOUT: !29 = !DILocation(line: 6, column: 5, scope: !27) +// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.b88d1103f417c6d4", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !31 = !DILocation(line: 12, column: 12, scope: !30) +// CHECK:STDOUT: !32 = !DILocation(line: 12, column: 5, scope: !30) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.e43630e9a6c38c3f", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !34 = !DILocation(line: 12, column: 12, scope: !33) +// CHECK:STDOUT: !35 = !DILocation(line: 12, column: 5, scope: !33) +// CHECK:STDOUT: !36 = distinct !DISubprogram(name: "GetT", linkageName: "_CGetT.C.Main.69a44aeea2e5c3f0", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !37 = !DILocation(line: 12, column: 12, scope: !36) +// CHECK:STDOUT: !38 = !DILocation(line: 12, column: 5, scope: !36) diff --git a/toolchain/lower/testdata/function/generic/call_basic.carbon b/toolchain/lower/testdata/function/generic/call_basic.carbon index aa13249c80f2..3cd375e6006f 100644 --- a/toolchain/lower/testdata/function/generic/call_basic.carbon +++ b/toolchain/lower/testdata/function/generic/call_basic.carbon @@ -179,7 +179,7 @@ fn M() { // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr void @_CH.Main.15b1f98bd9cc0c5b(ptr sret({}) %return, ptr %x) !dbg !76 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret ptr %x, !dbg !77 +// CHECK:STDOUT: ret void, !dbg !77 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives