mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:10:12 +01:00
Start supporting struct type literals in lowering. (#2822)
This currently handles most of struct types and member access, but not values. The value issue is, I think, an underlying semantic IR approach (changed in #2824). I'd still like to get this in in order to ensure I'm creating IR at least reasonably well, but I want to be clear this is expected to be incomplete and is split out mainly to try to keep PRs in reasonable units.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "toolchain/lowering/lowering_context.h"
|
||||
|
||||
#include "toolchain/semantics/semantics_ir.h"
|
||||
#include "toolchain/semantics/semantics_node_kind.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -47,19 +48,59 @@ auto LoweringContext::LowerBlock(SemanticsNodeBlockId block_id) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto LoweringContext::LowerNodeToType(SemanticsNodeId node_id) -> llvm::Type* {
|
||||
CARBON_CHECK(node_id.is_valid());
|
||||
auto LoweringContext::BuildLoweredNodeAsType(SemanticsNodeId node_id)
|
||||
-> llvm::Type* {
|
||||
switch (node_id.index) {
|
||||
case SemanticsBuiltinKind::EmptyStructType.AsInt():
|
||||
case SemanticsBuiltinKind::EmptyTuple.AsInt():
|
||||
// TODO: Should probably switch this to an actual empty tuple in the
|
||||
// future, but it's implemented as void for now.
|
||||
return builder_.getVoidTy();
|
||||
case SemanticsBuiltinKind::EmptyTupleType.AsInt():
|
||||
// Represent empty types as empty structs.
|
||||
// TODO: Investigate special-casing handling of these so that they can be
|
||||
// collectively replaced with LLVM's void, particularly around function
|
||||
// returns. LLVM doesn't allow declaring variables with a void type, so
|
||||
// that may require significant special casing.
|
||||
// TODO: Work to remove EmptyTuple here.
|
||||
return llvm::StructType::create(*llvm_context_,
|
||||
llvm::ArrayRef<llvm::Type*>());
|
||||
case SemanticsBuiltinKind::FloatingPointType.AsInt():
|
||||
// TODO: Handle different sizes.
|
||||
return builder_.getDoubleTy();
|
||||
case SemanticsBuiltinKind::IntegerType.AsInt():
|
||||
// TODO: Handle different sizes.
|
||||
return builder_.getInt32Ty();
|
||||
default:
|
||||
}
|
||||
|
||||
auto node = semantics_ir_->GetNode(node_id);
|
||||
switch (node.kind()) {
|
||||
case SemanticsNodeKind::StructType: {
|
||||
auto refs = semantics_ir_->GetNodeBlock(node.GetAsStructType().second);
|
||||
llvm::SmallVector<llvm::Type*> subtypes;
|
||||
subtypes.reserve(refs.size());
|
||||
for (auto ref_id : refs) {
|
||||
auto type_id = semantics_ir_->GetNode(ref_id).type_id();
|
||||
// TODO: Handle recursive types. The restriction for builtins prevents
|
||||
// recursion while still letting them cache.
|
||||
CARBON_CHECK(type_id.index < SemanticsBuiltinKind::ValidCount)
|
||||
<< type_id;
|
||||
subtypes.push_back(GetLoweredNodeAsType(type_id));
|
||||
}
|
||||
return llvm::StructType::create(*llvm_context_, subtypes);
|
||||
}
|
||||
default: {
|
||||
CARBON_FATAL() << "Cannot use node as type: " << node_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto LoweringContext::GetLoweredNodeAsType(SemanticsNodeId node_id)
|
||||
-> llvm::Type* {
|
||||
if (lowered_nodes_[node_id.index]) {
|
||||
return lowered_nodes_[node_id.index].get<llvm::Type*>();
|
||||
}
|
||||
|
||||
auto* type = BuildLoweredNodeAsType(node_id);
|
||||
lowered_nodes_[node_id.index] = type;
|
||||
return type;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -24,8 +24,25 @@ class LoweringContext {
|
||||
// the main execution loop.
|
||||
auto Run() -> std::unique_ptr<llvm::Module>;
|
||||
|
||||
// Returns a type for the given node.
|
||||
auto LowerNodeToType(SemanticsNodeId node_id) -> llvm::Type*;
|
||||
auto HasLoweredNode(SemanticsNodeId node_id) -> bool {
|
||||
return !lowered_nodes_[node_id.index].isNull();
|
||||
}
|
||||
|
||||
// Returns a type for the given node. May construct and cache the type
|
||||
// if it hasn't yet been built.
|
||||
auto GetLoweredNodeAsType(SemanticsNodeId node_id) -> llvm::Type*;
|
||||
|
||||
// Returns a value for the given node.
|
||||
auto GetLoweredNodeAsValue(SemanticsNodeId node_id) -> llvm::Value* {
|
||||
CARBON_CHECK(lowered_nodes_[node_id.index].is<llvm::Value*>())
|
||||
<< node_id << ": isNull == " << lowered_nodes_[node_id.index].isNull();
|
||||
return lowered_nodes_[node_id.index].get<llvm::Value*>();
|
||||
}
|
||||
// Sets the value for the given node.
|
||||
auto SetLoweredNodeAsValue(SemanticsNodeId node_id, llvm::Value* value) {
|
||||
CARBON_CHECK(lowered_nodes_[node_id.index].isNull()) << node_id;
|
||||
lowered_nodes_[node_id.index] = value;
|
||||
}
|
||||
|
||||
auto llvm_context() -> llvm::LLVMContext& { return *llvm_context_; }
|
||||
auto llvm_module() -> llvm::Module& { return *llvm_module_; }
|
||||
@@ -35,14 +52,15 @@ class LoweringContext {
|
||||
std::pair<llvm::BasicBlock*, SemanticsNodeBlockId>>& {
|
||||
return todo_blocks_;
|
||||
}
|
||||
auto lowered_nodes() -> llvm::SmallVector<llvm::Value*>& {
|
||||
return lowered_nodes_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Runs lowering for a block.
|
||||
auto LowerBlock(SemanticsNodeBlockId block_id) -> void;
|
||||
|
||||
// Builds the type for the given node, which should then be cached by the
|
||||
// caller.
|
||||
auto BuildLoweredNodeAsType(SemanticsNodeId node_id) -> llvm::Type*;
|
||||
|
||||
// State for building the LLVM IR.
|
||||
llvm::LLVMContext* llvm_context_;
|
||||
std::unique_ptr<llvm::Module> llvm_module_;
|
||||
@@ -58,12 +76,12 @@ class LoweringContext {
|
||||
// Maps nodes in SemanticsIR to a lowered value. This will have one entry per
|
||||
// node, and will be non-null when lowered. It's expected to be sparse during
|
||||
// execution because while expressions will have entries, statements won't.
|
||||
// TODO: This will probably become a PointerUnion of Value and Type.
|
||||
// TODO: Long-term, we should examine the practical trade-offs of making this
|
||||
// a map; a map may end up lower memory consumption, but a vector offers cache
|
||||
// efficiency and better performance. As a consequence, the right choice is
|
||||
// unclear.
|
||||
llvm::SmallVector<llvm::Value*> lowered_nodes_;
|
||||
llvm::SmallVector<llvm::PointerUnion<llvm::Type*, llvm::Value*>>
|
||||
lowered_nodes_;
|
||||
};
|
||||
|
||||
// Declare handlers for each SemanticsIR node.
|
||||
|
||||
@@ -18,10 +18,17 @@ auto LoweringHandleCrossReference(LoweringContext& /*context*/,
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
}
|
||||
|
||||
auto LoweringHandleAssign(LoweringContext& /*context*/,
|
||||
SemanticsNodeId /*node_id*/, SemanticsNode node)
|
||||
-> void {
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
auto LoweringHandleAssign(LoweringContext& context, SemanticsNodeId /*node_id*/,
|
||||
SemanticsNode node) -> void {
|
||||
auto [storage_id, value_id] = node.GetAsAssign();
|
||||
if (value_id == SemanticsNodeId::BuiltinEmptyStruct ||
|
||||
value_id == SemanticsNodeId::BuiltinEmptyTuple) {
|
||||
// Elide the 0-length store; these have no value assigned and it should have
|
||||
// no effect.
|
||||
return;
|
||||
}
|
||||
context.builder().CreateStore(context.GetLoweredNodeAsValue(value_id),
|
||||
context.GetLoweredNodeAsValue(storage_id));
|
||||
}
|
||||
|
||||
auto LoweringHandleBinaryOperatorAdd(LoweringContext& /*context*/,
|
||||
@@ -31,9 +38,9 @@ auto LoweringHandleBinaryOperatorAdd(LoweringContext& /*context*/,
|
||||
}
|
||||
|
||||
auto LoweringHandleBindName(LoweringContext& /*context*/,
|
||||
SemanticsNodeId /*node_id*/, SemanticsNode node)
|
||||
SemanticsNodeId /*node_id*/, SemanticsNode /*node*/)
|
||||
-> void {
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
// Probably need to do something here, but not necessary for now.
|
||||
}
|
||||
|
||||
auto LoweringHandleBuiltin(LoweringContext& /*context*/,
|
||||
@@ -65,11 +72,11 @@ auto LoweringHandleFunctionDeclaration(LoweringContext& context,
|
||||
llvm::SmallVector<llvm::Type*> args;
|
||||
args.resize_for_overwrite(param_refs.size());
|
||||
for (int i = 0; i < static_cast<int>(param_refs.size()); ++i) {
|
||||
args[i] = context.LowerNodeToType(
|
||||
args[i] = context.GetLoweredNodeAsType(
|
||||
context.semantics_ir().GetNode(param_refs[i]).type_id());
|
||||
}
|
||||
|
||||
llvm::Type* return_type = context.LowerNodeToType(
|
||||
llvm::Type* return_type = context.GetLoweredNodeAsType(
|
||||
callable.return_type_id.is_valid() ? callable.return_type_id
|
||||
: SemanticsNodeId::BuiltinEmptyTuple);
|
||||
llvm::FunctionType* function_type =
|
||||
@@ -108,9 +115,8 @@ auto LoweringHandleIntegerLiteral(LoweringContext& context,
|
||||
-> void {
|
||||
SemanticsIntegerLiteralId int_id = node.GetAsIntegerLiteral();
|
||||
llvm::APInt i = context.semantics_ir().GetIntegerLiteral(int_id);
|
||||
llvm::Value* v = llvm::ConstantInt::get(context.builder().getInt32Ty(),
|
||||
i.getLimitedValue());
|
||||
context.lowered_nodes()[node_id.index] = v;
|
||||
llvm::Value* v = context.builder().getInt32(i.getLimitedValue());
|
||||
context.SetLoweredNodeAsValue(node_id, v);
|
||||
}
|
||||
|
||||
auto LoweringHandleRealLiteral(LoweringContext& /*context*/,
|
||||
@@ -128,7 +134,7 @@ auto LoweringHandleReturnExpression(LoweringContext& context,
|
||||
SemanticsNodeId /*node_id*/,
|
||||
SemanticsNode node) -> void {
|
||||
SemanticsNodeId expr_id = node.GetAsReturnExpression();
|
||||
context.builder().CreateRet(context.lowered_nodes()[expr_id.index]);
|
||||
context.builder().CreateRet(context.GetLoweredNodeAsValue(expr_id));
|
||||
}
|
||||
|
||||
auto LoweringHandleStringLiteral(LoweringContext& /*context*/,
|
||||
@@ -137,16 +143,22 @@ auto LoweringHandleStringLiteral(LoweringContext& /*context*/,
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
}
|
||||
|
||||
auto LoweringHandleStructMemberAccess(LoweringContext& /*context*/,
|
||||
SemanticsNodeId /*node_id*/,
|
||||
auto LoweringHandleStructMemberAccess(LoweringContext& context,
|
||||
SemanticsNodeId node_id,
|
||||
SemanticsNode node) -> void {
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
auto [struct_id, member_index] = node.GetAsStructMemberAccess();
|
||||
auto* struct_type = context.GetLoweredNodeAsType(
|
||||
context.semantics_ir().GetNode(struct_id).type_id());
|
||||
auto* gep = context.builder().CreateStructGEP(
|
||||
struct_type, context.GetLoweredNodeAsValue(struct_id),
|
||||
member_index.index);
|
||||
context.SetLoweredNodeAsValue(node_id, gep);
|
||||
}
|
||||
|
||||
auto LoweringHandleStructType(LoweringContext& /*context*/,
|
||||
SemanticsNodeId /*node_id*/, SemanticsNode node)
|
||||
-> void {
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
SemanticsNodeId /*node_id*/,
|
||||
SemanticsNode /*node*/) -> void {
|
||||
// No action to take.
|
||||
}
|
||||
|
||||
auto LoweringHandleStructTypeField(LoweringContext& /*context*/,
|
||||
@@ -155,10 +167,22 @@ auto LoweringHandleStructTypeField(LoweringContext& /*context*/,
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
}
|
||||
|
||||
auto LoweringHandleStructValue(LoweringContext& /*context*/,
|
||||
SemanticsNodeId /*node_id*/, SemanticsNode node)
|
||||
auto LoweringHandleStructValue(LoweringContext& context,
|
||||
SemanticsNodeId node_id, SemanticsNode node)
|
||||
-> void {
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
auto* type = context.GetLoweredNodeAsType(node.type_id());
|
||||
auto* alloca = context.builder().CreateAlloca(type);
|
||||
context.SetLoweredNodeAsValue(node_id, alloca);
|
||||
|
||||
// TODO: Figure out changes to flow so that values are calculated for store.
|
||||
// Right now, the struct value IR is unevaluated.
|
||||
// auto refs =
|
||||
// context.semantics_ir().GetNodeBlock(node.GetAsStructValue().second);
|
||||
// for (int i = 0; i < static_cast<int>(refs.size()); ++i) {
|
||||
// auto* gep = context.builder().CreateStructGEP(type, alloca, i);
|
||||
// context.builder().CreateStore(context.GetLoweredNodeAsValue(refs[i]),
|
||||
// gep);
|
||||
// }
|
||||
}
|
||||
|
||||
auto LoweringHandleStubReference(LoweringContext& /*context*/,
|
||||
@@ -167,10 +191,16 @@ auto LoweringHandleStubReference(LoweringContext& /*context*/,
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
}
|
||||
|
||||
auto LoweringHandleVarStorage(LoweringContext& /*context*/,
|
||||
SemanticsNodeId /*node_id*/, SemanticsNode node)
|
||||
-> void {
|
||||
CARBON_FATAL() << "TODO: Add support: " << node;
|
||||
auto LoweringHandleVarStorage(LoweringContext& context, SemanticsNodeId node_id,
|
||||
SemanticsNode node) -> void {
|
||||
// TODO: This doesn't handle globals. Also, LLVM requires globals to have a
|
||||
// name. Do we want to generate a name, which would need to be consistent
|
||||
// across translation units, or use the given name, which requires either
|
||||
// looking ahead for BindName or restructuring semantics, either of which
|
||||
// affects the destructuring due to the difference in storage?
|
||||
auto* alloca = context.builder().CreateAlloca(
|
||||
context.GetLoweredNodeAsType(node.type_id()));
|
||||
context.SetLoweredNodeAsValue(node_id, alloca);
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/function/definition/params_one.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/function/definition/params_one.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @Foo(i32 %a) {
|
||||
// CHECK:STDOUT: %0 = type {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define %0 @Foo(i32 %a) {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/function/definition/params_two.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/function/definition/params_two.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @Foo(i32 %a, i32 %b) {
|
||||
// CHECK:STDOUT: %0 = type {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define %0 @Foo(i32 %a, i32 %b) {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/function/definition/params_zero.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/function/definition/params_zero.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @Foo() {
|
||||
// CHECK:STDOUT: %0 = type {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define %0 @Foo() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
|
||||
+3
-1
@@ -6,7 +6,9 @@
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/return/no_value.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/return/no_value.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @Main() {
|
||||
// CHECK:STDOUT: %0 = type {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define %0 @Main() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/struct/empty.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/struct/empty.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: %0 = type {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define i32 @Run() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %0 = alloca %0, align 8
|
||||
// CHECK:STDOUT: %1 = alloca %0, align 8
|
||||
// CHECK:STDOUT: store ptr %0, ptr %1, align 8
|
||||
// CHECK:STDOUT: ret i32 0
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
fn Run() -> i32 {
|
||||
var x: {} = {};
|
||||
var y: {} = x;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/struct/member_access.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/struct/member_access.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: %0 = type { double, i32 }
|
||||
// CHECK:STDOUT: %1 = type { double, i32 }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define i32 @Run() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %0 = alloca %0, align 8
|
||||
// CHECK:STDOUT: %1 = alloca %1, align 8
|
||||
// CHECK:STDOUT: store ptr %1, ptr %0, align 8
|
||||
// CHECK:STDOUT: %2 = alloca i32, align 4
|
||||
// CHECK:STDOUT: %3 = getelementptr inbounds %0, ptr %0, i32 0, i32 1
|
||||
// CHECK:STDOUT: store ptr %3, ptr %2, align 8
|
||||
// CHECK:STDOUT: %4 = alloca i32, align 4
|
||||
// CHECK:STDOUT: store ptr %2, ptr %4, align 8
|
||||
// CHECK:STDOUT: ret i32 0
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
fn Run() -> i32 {
|
||||
var x: {.a: f64, .b: i32} = {.a = 0.0, .b = 1};
|
||||
var y: i32 = x.b;
|
||||
var z: i32 = y;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/struct/one_entry.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/struct/one_entry.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: %0 = type { i32 }
|
||||
// CHECK:STDOUT: %1 = type { i32 }
|
||||
// CHECK:STDOUT: %2 = type { i32 }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define i32 @Run() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %0 = alloca %0, align 8
|
||||
// CHECK:STDOUT: %1 = alloca %1, align 8
|
||||
// CHECK:STDOUT: store ptr %1, ptr %0, align 8
|
||||
// CHECK:STDOUT: %2 = alloca %2, align 8
|
||||
// CHECK:STDOUT: store ptr %0, ptr %2, align 8
|
||||
// CHECK:STDOUT: ret i32 0
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
fn Run() -> i32 {
|
||||
var x: {.a: i32} = {.a = 4};
|
||||
var y: {.a: i32} = x;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/struct/two_entries.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/struct/two_entries.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: %0 = type { i32, i32 }
|
||||
// CHECK:STDOUT: %1 = type { i32, i32 }
|
||||
// CHECK:STDOUT: %2 = type { i32, i32 }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define i32 @Run() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %0 = alloca %0, align 8
|
||||
// CHECK:STDOUT: %1 = alloca %1, align 8
|
||||
// CHECK:STDOUT: store ptr %1, ptr %0, align 8
|
||||
// CHECK:STDOUT: %2 = alloca %2, align 8
|
||||
// CHECK:STDOUT: store ptr %0, ptr %2, align 8
|
||||
// CHECK:STDOUT: ret i32 0
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
fn Run() -> i32 {
|
||||
var x: {.a: i32, .b: i32} = {.a = 1, .b = 2};
|
||||
var y: {.a: i32, .b: i32} = x;
|
||||
return 0;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: ; ModuleID = 'toolchain/lowering/testdata/var/local.carbon'
|
||||
// CHECK:STDOUT: source_filename = "toolchain/lowering/testdata/var/local.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define i32 @Run() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %0 = alloca i32, align 4
|
||||
// CHECK:STDOUT: store i32 1, ptr %0, align 4
|
||||
// CHECK:STDOUT: ret ptr %0
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
fn Run() -> i32 {
|
||||
var x: i32 = 1;
|
||||
return x;
|
||||
}
|
||||
Reference in New Issue
Block a user