Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start supporting struct type literals in lowering. #2822

Merged
merged 45 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c7bbb41
Add framework for replacing lit with cc_test
jonmeow May 4, 2023
d963311
Refactor parser logic into separate files.
jonmeow May 4, 2023
c4a2f9c
Fix pretty stack tracing
jonmeow May 8, 2023
bc9de0d
Remove handle_states.h
jonmeow May 8, 2023
735b361
x
jonmeow May 8, 2023
41b90b5
drafting
jonmeow May 8, 2023
ae393c5
drafting
jonmeow May 8, 2023
196f319
x
jonmeow May 8, 2023
f681741
x
jonmeow May 8, 2023
d4d1d6d
Refactoring lowering logic into separate files.
jonmeow May 8, 2023
3b6c751
x
jonmeow May 9, 2023
f1252c7
x
jonmeow May 9, 2023
b219add
Path handling
jonmeow May 9, 2023
6a236e9
Merge branch 'test-framework' into test-toolchain
jonmeow May 9, 2023
e769e01
tests
jonmeow May 9, 2023
a100082
x
jonmeow May 9, 2023
c41e6bb
fail_example
jonmeow May 9, 2023
c845622
Merge branch 'test-framework' into test-toolchain
jonmeow May 9, 2023
fd82dde
x
jonmeow May 9, 2023
25c75c2
x
jonmeow May 9, 2023
2f89094
Merge branch 'test-framework' into test-toolchain
jonmeow May 9, 2023
f285b6b
x
jonmeow May 9, 2023
09f21e9
Merge branch 'lower-factor' into struct-work
jonmeow May 10, 2023
6e38274
Merge branch 'parser-factor' into struct-work
jonmeow May 10, 2023
b4add49
Merge branch 'sem-factor' into struct-work
jonmeow May 10, 2023
1fa126a
x
jonmeow May 11, 2023
cc4c226
Update testing/file_test/file_test_base.cpp
jonmeow May 12, 2023
4a9d32b
x
jonmeow May 12, 2023
542cefe
erge branch 'test-framework' into test-toolchain
jonmeow May 12, 2023
c91309f
Merge branch 'test-toolchain' into struct-work
jonmeow May 12, 2023
7432bf1
comments
jonmeow May 12, 2023
ffa46bb
Merge branch 'parser-factor' into struct-work
jonmeow May 12, 2023
cd0ff1a
merge
jonmeow May 12, 2023
0d65930
Merge branch 'test-toolchain' into struct-work
jonmeow May 12, 2023
eb95e3a
x
jonmeow May 12, 2023
b53eeec
comments
jonmeow May 12, 2023
4954a78
x
jonmeow May 12, 2023
0a81b15
Merge branch 'lower-factor' into struct-work
jonmeow May 12, 2023
0115ca2
Merge branch 'sem-factor' into struct-work
jonmeow May 12, 2023
440f03f
Merge branch 'trunk' into test-toolchain
jonmeow May 12, 2023
d1dfba1
Merge branch 'test-toolchain' into struct-work
jonmeow May 12, 2023
ab9296e
merge
jonmeow May 15, 2023
52e0855
x
jonmeow May 16, 2023
5c5369d
struct
jonmeow May 17, 2023
ef8520e
comment
jonmeow May 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions toolchain/lowering/lowering_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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
32 changes: 25 additions & 7 deletions toolchain/lowering/lowering_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }
Expand All @@ -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_;
Expand All @@ -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.
Expand Down
80 changes: 55 additions & 25 deletions toolchain/lowering/lowering_handle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*/,
Expand All @@ -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*/,
Expand Down Expand Up @@ -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(
chandlerc marked this conversation as resolved.
Show resolved Hide resolved
callable.return_type_id.is_valid() ? callable.return_type_id
: SemanticsNodeId::BuiltinEmptyTuple);
llvm::FunctionType* function_type =
Expand Down Expand Up @@ -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*/,
Expand All @@ -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*/,
Expand All @@ -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*/,
Expand All @@ -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*/,
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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: }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: }

Expand Down
4 changes: 3 additions & 1 deletion toolchain/lowering/testdata/return/no_value.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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: }
Expand Down
23 changes: 23 additions & 0 deletions toolchain/lowering/testdata/struct/empty.carbon
Original file line number Diff line number Diff line change
@@ -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;
}
30 changes: 30 additions & 0 deletions toolchain/lowering/testdata/struct/member_access.carbon
Original file line number Diff line number Diff line change
@@ -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;
}
Loading