mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-15 08:58:29 +00:00
When building an expression referencing the result of name lookup, if the lookup result is non-constant then check we can access it from the current scope. * If it is a global, then use a new `GlobalRef` instruction to get the value of that global. In lowering, create global variables to hold the values of global `let` bindings so that `GlobalRef` can access those values. * If it is a local, walk the lexical scopes between it and the point of use and ensure it is usable through from all of them. Error if there's an intervening declarative scope such as a class, add a TODO to handle captures for intervening functions, and allow `typeof(...)` to name things it can't read the value of since it's unevaluated. Global `let`s (which includes `let`s at class scope) now also have their initializers collected into the global inits block. If the initializer is non-constant, the wrapper binding also lives in the global inits block; otherwise it continues to live in the file block and the variable emitted for it is a global constant. Assisted-by: Gemini and Claude via Antigravity
513 lines
20 KiB
C++
513 lines
20 KiB
C++
// 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 "llvm/ADT/APFloat.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/IR/BasicBlock.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/Type.h"
|
|
#include "llvm/IR/Value.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "toolchain/lower/function_context.h"
|
|
#include "toolchain/sem_ir/builtin_function_kind.h"
|
|
#include "toolchain/sem_ir/expr_info.h"
|
|
#include "toolchain/sem_ir/function.h"
|
|
#include "toolchain/sem_ir/import_ir.h"
|
|
#include "toolchain/sem_ir/inst.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
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().GetTypeInstId(
|
|
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));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::ArrayIndex inst) -> void {
|
|
auto* array_value = context.GetValue(inst.array_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;
|
|
auto index_type = context.GetTypeIdOfInst(inst.index_id);
|
|
if (index_type.file->types().GetTypeInstId(index_type.type_id) ==
|
|
SemIR::IntLiteralType::TypeInstId) {
|
|
auto value = context.sem_ir().insts().GetAs<SemIR::IntValue>(
|
|
context.sem_ir().constant_values().GetConstantInstId(inst.index_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);
|
|
}
|
|
|
|
llvm::Value* indexes[2] = {
|
|
llvm::ConstantInt::get(llvm::Type::getInt32Ty(context.llvm_context()), 0),
|
|
index};
|
|
context.SetLocal(inst_id,
|
|
context.builder().CreateInBoundsGEP(llvm_type, array_value,
|
|
indexes, "array.index"));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::ArrayInit inst) -> void {
|
|
// The result of initialization is the return slot of the initializer.
|
|
context.SetLocal(inst_id, context.GetValue(inst.dest_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::AsCompatible inst) -> void {
|
|
context.SetLocal(inst_id, context.GetValue(inst.source_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
|
SemIR::Assign inst) -> void {
|
|
if (SemIR::GetExprCategory(context.sem_ir(), inst.rhs_id) !=
|
|
SemIR::ExprCategory::InPlaceInitializing) {
|
|
context.InitializeStorage(context.GetTypeIdOfInst(inst.lhs_id), inst.lhs_id,
|
|
inst.rhs_id);
|
|
}
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::AliasBinding inst) -> void {
|
|
if (IsNamespace(context, inst_id)) {
|
|
return;
|
|
}
|
|
|
|
context.SetLocal(inst_id, context.GetValue(inst.value_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::CppAddrOfFunction inst) -> void {
|
|
context.SetLocal(
|
|
inst_id, context.specific_file_context().GetFunction(inst.function_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::ExportDecl inst) -> void {
|
|
if (IsNamespace(context, inst_id)) {
|
|
return;
|
|
}
|
|
|
|
context.SetLocal(inst_id, context.GetValue(inst.value_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::SymbolicBinding inst) -> void {
|
|
context.SetLocal(inst_id, context.GetValue(inst.value_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::WrapperBinding inst) -> void {
|
|
auto* value = context.GetValue(inst.value_id);
|
|
context.SetLocal(inst_id, value);
|
|
|
|
const auto& entity_name =
|
|
context.sem_ir().entity_names().Get(inst.entity_name_id);
|
|
if (!entity_name.parent_scope_id.has_value() ||
|
|
entity_name.name_id == SemIR::NameId::Underscore ||
|
|
context.sem_ir().constant_values().Get(inst_id).is_constant()) {
|
|
return;
|
|
}
|
|
|
|
auto& file_context = context.GetFileContext(&context.sem_ir());
|
|
auto* global_var = file_context.GetOrCreateGlobalBinding(inst_id);
|
|
if (!global_var) {
|
|
return;
|
|
}
|
|
global_var->setInitializer(
|
|
llvm::Constant::getNullValue(global_var->getValueType()));
|
|
|
|
if (SemIR::IsRefCategory(SemIR::GetExprCategory(context.sem_ir(), inst_id))) {
|
|
auto* store = context.builder().CreateStore(value, global_var);
|
|
store->setAlignment(
|
|
context.llvm_module().getDataLayout().getPointerABIAlignment(0));
|
|
return;
|
|
}
|
|
|
|
auto value_rep = SemIR::ValueRepr::ForType(context.sem_ir(), inst.type_id);
|
|
switch (value_rep.kind) {
|
|
case SemIR::ValueRepr::Copy:
|
|
context.StoreObject(
|
|
{.file = &context.sem_ir(), .type_id = value_rep.type_id}, value,
|
|
global_var);
|
|
break;
|
|
case SemIR::ValueRepr::Pointer: {
|
|
auto pointee_type_id = context.sem_ir().GetPointeeType(value_rep.type_id);
|
|
context.CopyObject(
|
|
{.file = &context.sem_ir(), .type_id = pointee_type_id}, value,
|
|
global_var);
|
|
break;
|
|
}
|
|
default:
|
|
CARBON_FATAL("Unexpected value repr for global binding: {0}", inst);
|
|
}
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::GlobalRef inst) -> void {
|
|
auto [canonical_ir, canonical_inst_id] =
|
|
SemIR::GetCanonicalFileAndInstId(&context.sem_ir(), inst.inst_id);
|
|
auto& target_file_context = context.GetFileContext(canonical_ir);
|
|
auto* global_var =
|
|
target_file_context.GetOrCreateGlobalBinding(canonical_inst_id);
|
|
context.AddGlobalToCurrentFingerprint(global_var);
|
|
|
|
if (SemIR::IsRefCategory(
|
|
SemIR::GetExprCategory(*canonical_ir, canonical_inst_id))) {
|
|
auto* ptr_type = llvm::PointerType::get(context.llvm_context(),
|
|
/*AddressSpace=*/0);
|
|
auto* load = context.builder().CreateLoad(ptr_type, global_var);
|
|
load->setAlignment(
|
|
context.llvm_module().getDataLayout().getPointerABIAlignment(0));
|
|
context.SetLocal(inst_id, load);
|
|
return;
|
|
}
|
|
|
|
auto canonical_binding =
|
|
canonical_ir->insts().GetAs<SemIR::WrapperBinding>(canonical_inst_id);
|
|
auto value_rep =
|
|
SemIR::ValueRepr::ForType(*canonical_ir, canonical_binding.type_id);
|
|
switch (value_rep.kind) {
|
|
case SemIR::ValueRepr::Unknown:
|
|
case SemIR::ValueRepr::Dependent:
|
|
CARBON_FATAL("Unexpected incomplete or dependent global binding: {0}",
|
|
canonical_binding);
|
|
case SemIR::ValueRepr::None:
|
|
context.SetLocal(
|
|
inst_id,
|
|
llvm::PoisonValue::get(context.GetType(
|
|
{.file = canonical_ir, .type_id = canonical_binding.type_id})));
|
|
break;
|
|
case SemIR::ValueRepr::Copy:
|
|
context.SetLocal(inst_id,
|
|
context.LoadObject(
|
|
{.file = canonical_ir, .type_id = value_rep.type_id},
|
|
global_var));
|
|
break;
|
|
case SemIR::ValueRepr::Pointer:
|
|
context.SetLocal(inst_id, global_var);
|
|
break;
|
|
case SemIR::ValueRepr::Custom:
|
|
CARBON_FATAL("TODO: Support global binding with custom value repr");
|
|
}
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::BlockArg inst) -> void {
|
|
context.SetLocal(
|
|
inst_id,
|
|
context.GetBlockArg(inst.block_id, context.GetTypeIdOfInst(inst_id)));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::BoundMethod inst) -> void {
|
|
// Propagate just the function; the object is separately provided to the
|
|
// enclosing call as an implicit argument.
|
|
context.SetLocal(inst_id, context.GetValue(inst.function_decl_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
|
SemIR::Branch inst) -> void {
|
|
// Opportunistically avoid creating a BasicBlock that contains just a branch.
|
|
// TODO: Don't do this if it would remove a loop preheader block.
|
|
llvm::BasicBlock* block = context.builder().GetInsertBlock();
|
|
if (block->empty() && context.TryToReuseBlock(inst.target_id, block)) {
|
|
// Reuse this block as the branch target.
|
|
} else {
|
|
context.builder().CreateBr(context.GetBlock(inst.target_id));
|
|
}
|
|
|
|
context.builder().ClearInsertionPoint();
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
|
SemIR::BranchIf inst) -> void {
|
|
llvm::Value* cond = context.GetValue(inst.cond_id);
|
|
llvm::BasicBlock* then_block = context.GetBlock(inst.target_id);
|
|
llvm::BasicBlock* else_block = context.MakeSyntheticBlock();
|
|
context.builder().CreateCondBr(cond, then_block, else_block);
|
|
context.builder().SetInsertPoint(else_block);
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
|
SemIR::BranchWithArg inst) -> void {
|
|
llvm::Value* arg = context.GetValue(inst.arg_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
|
|
// predecessor, so that we can correctly populate the predecessors of the
|
|
// PHINode.
|
|
llvm::BasicBlock* block = context.builder().GetInsertBlock();
|
|
llvm::BasicBlock* phi_predecessor = block;
|
|
if (block->empty() && context.IsCurrentSyntheticBlock(block) &&
|
|
context.TryToReuseBlock(inst.target_id, block)) {
|
|
// Reuse this block as the branch target.
|
|
phi_predecessor = block->getSinglePredecessor();
|
|
CARBON_CHECK(phi_predecessor,
|
|
"Synthetic block did not have a single predecessor");
|
|
} else {
|
|
context.builder().CreateBr(context.GetBlock(inst.target_id));
|
|
}
|
|
|
|
context.GetBlockArg(inst.target_id, arg_type)
|
|
->addIncoming(arg, phi_predecessor);
|
|
context.builder().ClearInsertionPoint();
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::Converted inst) -> void {
|
|
context.SetLocal(inst_id, context.GetValue(inst.result_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::Deref inst) -> void {
|
|
context.SetLocal(inst_id, context.GetValue(inst.pointer_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::FacetValue /*inst*/) -> void {
|
|
context.SetLocal(inst_id, context.GetTypeAsValue());
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
|
SemIR::InPlaceInit inst) -> void {
|
|
context.InitializeStorage(context.GetTypeIdOfInst(inst.dest_id), inst.dest_id,
|
|
inst.src_id);
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
|
|
SemIR::NameBindingDecl /*inst*/) -> void {
|
|
// A NameBindingDecl is lowered by pattern matching.
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::NameRef inst) -> void {
|
|
if (IsNamespace(context, inst_id)) {
|
|
return;
|
|
}
|
|
|
|
context.SetLocal(inst_id, context.GetValue(inst.value_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
|
|
SemIR::OutParam /*inst*/) -> void {
|
|
// Parameters are lowered by `BuildFunctionDefinition`.
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
|
|
SemIR::RefParam /*inst*/) -> void {
|
|
// Parameters are lowered by `BuildFunctionDefinition`.
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
|
|
SemIR::ValueParam /*inst*/) -> void {
|
|
// Parameters are lowered by `BuildFunctionDefinition`.
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::RefTagExpr inst) -> void {
|
|
context.SetLocal(inst_id, context.GetValue(inst.expr_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::ReturnSlot inst) -> void {
|
|
context.SetLocal(inst_id, context.GetValue(inst.storage_id));
|
|
}
|
|
|
|
// Returns how the function being lowered returns its result, as determined
|
|
// when its signature was lowered.
|
|
static auto GetReturnKind(FunctionContext& context) -> ReturnKind {
|
|
const auto& function_info = context.specific_file_context().GetFunctionInfo(
|
|
context.specific_sem_ir_function_id(), context.specific_id());
|
|
CARBON_CHECK(function_info, "Lowering body of undeclared function");
|
|
return function_info->return_kind;
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
|
SemIR::Return /*inst*/) -> void {
|
|
// The 'Run()' entry point does not need to specify a return type, but
|
|
// the C runtime and system ABI expects the entry point to return an `int`.
|
|
// In this situation we modify the lowered IR to return `0`
|
|
// with the expected LLVM type that corresponds to the `int` type.
|
|
if (GetReturnKind(context) == ReturnKind::EntryPointInt32) {
|
|
context.builder().CreateRet(context.builder().getInt32(0));
|
|
return;
|
|
}
|
|
context.builder().CreateRetVoid();
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
|
SemIR::ReturnExpr inst) -> void {
|
|
auto expr_cat =
|
|
SemIR::GetExprCategory(context.sem_ir(), inst.expr_id,
|
|
&context.specific_sem_ir(), context.specific_id());
|
|
context.AddEnumToCurrentFingerprint(expr_cat);
|
|
|
|
auto return_kind = GetReturnKind(context);
|
|
context.AddEnumToCurrentFingerprint(return_kind);
|
|
switch (return_kind) {
|
|
case ReturnKind::None:
|
|
// Nothing to return.
|
|
context.builder().CreateRetVoid();
|
|
return;
|
|
|
|
case ReturnKind::ByReference:
|
|
// Return a pointer: either the address of a reference, or the value
|
|
// representation of a value of a type with a pointer (or pointer-like
|
|
// custom) value representation.
|
|
context.builder().CreateRet(context.GetValue(inst.expr_id));
|
|
return;
|
|
|
|
case ReturnKind::ByCopy: {
|
|
auto* value = context.GetValue(inst.expr_id);
|
|
if (expr_cat == SemIR::ExprCategory::InPlaceInitializing) {
|
|
value = context.builder().CreateLoad(
|
|
context.GetType(context.GetTypeIdOfInst(inst.expr_id)), value);
|
|
}
|
|
context.builder().CreateRet(value);
|
|
return;
|
|
}
|
|
|
|
case ReturnKind::InPlace: {
|
|
auto result_type = context.GetTypeIdOfInst(inst.expr_id);
|
|
CARBON_CHECK(context.GetValueRepr(result_type).repr.kind ==
|
|
SemIR::ValueRepr::Pointer,
|
|
"TODO: Add support for ReturnExpr with custom value repr");
|
|
context.InitializeStorage(result_type, inst.dest_id, inst.expr_id);
|
|
context.builder().CreateRetVoid();
|
|
return;
|
|
}
|
|
|
|
case ReturnKind::EntryPointInt32:
|
|
CARBON_FATAL(
|
|
"`return` with expression in entry point without a return "
|
|
"type");
|
|
}
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::RefReturn /*inst*/) -> void {
|
|
// A `RefReturn` is a placeholder that represents the absence of a storage
|
|
// location, so it should never actually be used, but in some cases it will
|
|
// be propagated, so we poison it.
|
|
context.SetLocal(inst_id,
|
|
llvm::PoisonValue::get(context.GetTypeOfInst(inst_id)));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
|
|
SemIR::SpecificFunction /*inst*/) -> void {
|
|
// Nothing to do. This value should never be consumed.
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
|
|
SemIR::SpecificImplFunction /*inst*/) -> void {
|
|
// Nothing to do. This value should never be consumed.
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::SpecificInst inst) -> void {
|
|
// If it's valid to refer to inst.inst_id at runtime and it's not constant, it
|
|
// must be within this function, and hence in the same specific that we're
|
|
// lowering.
|
|
CARBON_CHECK(&context.sem_ir() == &context.specific_sem_ir() &&
|
|
context.specific_id() == inst.specific_id,
|
|
"Runtime specific_inst refers to a different specific");
|
|
|
|
// When lowering templates, the specific may contain instructions that
|
|
// neither emit a value during lowering nor have a constant value, so set
|
|
// require_value=false.
|
|
if (auto* value = context.GetValue(inst.inst_id, /*require_value=*/false)) {
|
|
context.SetLocal(inst_id, value);
|
|
}
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::SpliceBlock inst) -> void {
|
|
context.LowerBlockContents(inst.block_id);
|
|
context.SetLocal(inst_id, context.GetValue(inst.result_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::SpliceInst inst) -> void {
|
|
auto [inst_ir, inst_value_id] = GetConstantValueInSpecific(
|
|
context.specific_sem_ir(), context.specific_id(), context.sem_ir(),
|
|
inst.inst_id);
|
|
auto inst_value =
|
|
inst_ir->constant_values().GetInstAs<SemIR::InstValue>(inst_value_id);
|
|
if (inst_ir == &context.sem_ir()) {
|
|
// Easy case: same file. Just emit the spliced instruction.
|
|
context.AddInstToCurrentFingerprint(inst_value.inst_id);
|
|
context.LowerInst(inst_value.inst_id);
|
|
context.SetLocal(inst_id, context.GetValue(inst_value.inst_id));
|
|
} else {
|
|
// TODO: We don't yet have support for cross-file templates at all. Lowering
|
|
// the result here would mostly just require that we switch the `sem_ir` on
|
|
// the `FunctionContext` to instead refer to the `specific_sem_ir`, but we
|
|
// would also need to handle references from the instantiated instructions
|
|
// back to the original ones, and we don't even know how to represent those
|
|
// references yet.
|
|
CARBON_FATAL("Cross-file template lowering not implemented yet");
|
|
}
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::TypeLiteral inst) -> void {
|
|
context.SetLocal(inst_id, context.GetValue(inst.value_id));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::UnaryOperatorNot inst) -> void {
|
|
context.SetLocal(
|
|
inst_id, context.builder().CreateNot(context.GetValue(inst.operand_id)));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::UpdateInit inst) -> void {
|
|
// Ensure that our subordinate initializations have been performed. They may
|
|
// have been skipped if they were constant.
|
|
context.InitializeStorage(inst.base_init_id);
|
|
context.InitializeStorage(inst.update_init_id);
|
|
|
|
// TODO: Add a helper to poison a value slot.
|
|
context.SetLocal(inst_id,
|
|
llvm::PoisonValue::get(context.GetTypeOfInst(inst_id)));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::ValueReturn /*inst*/) -> void {
|
|
// A `ValueReturn` is a placeholder that represents the absence of a storage
|
|
// location, so it should never actually be used, but in some cases it will
|
|
// be propagated, so we poison it.
|
|
context.SetLocal(inst_id,
|
|
llvm::PoisonValue::get(context.GetTypeOfInst(inst_id)));
|
|
}
|
|
|
|
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
|
SemIR::VarStorage /* inst */) -> void {
|
|
context.SetLocal(inst_id,
|
|
context.CreateAlloca(context.GetTypeIdOfInst(inst_id)));
|
|
}
|
|
|
|
} // namespace Carbon::Lower
|