Lowering of Branch / BranchIf / BranchWithArg / BlockArg. (#2904)

This gives us complete lowering of `if` expressions plus `and` and `or`.

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Richard Smith
2023-06-15 17:53:07 -07:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent c27f41c94b
commit 0d4d392d12
44 changed files with 453 additions and 91 deletions
+1
View File
@@ -27,6 +27,7 @@ cc_library(
"lowering_handle.cpp",
],
hdrs = [
"lowering_block_worklist.h",
"lowering_context.h",
"lowering_function_context.h",
],
@@ -0,0 +1,52 @@
// 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
#ifndef CARBON_TOOLCHAIN_LOWERING_LOWERING_BLOCK_WORKLIST_H_
#define CARBON_TOOLCHAIN_LOWERING_LOWERING_BLOCK_WORKLIST_H_
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "toolchain/semantics/semantics_node.h"
namespace Carbon {
// A worklist for blocks that need to be lowered.
//
// The blocks form a tree, where the sequence of blocks that are pushed
// following a `Pop` that returned block B are the children of B. Blocks are
// popped in a preorder depth-first traversal over this tree, where blocks that
// are children of the same block are popped in the same order in which they
// were pushed.
//
// This traversal order is intended to produce readable IR:
//
// - In the absence of control flow back-edges, branches will typically branch
// to blocks emitted later, although this is not guaranteed.
// - A branch and the blocks that it branches to will typically be placed close
// together.
class LoweringBlockWorklist {
public:
// Add a block to the work list.
auto Push(SemanticsNodeBlockId id) -> void { worklist_.push_back(id); }
// Pop the next block to lower.
auto Pop() -> SemanticsNodeBlockId {
// Reverse the order of the blocks added since the last `Pop`, so that we
// pop them in the order that they were `Pushed` in.
std::reverse(worklist_.begin() + size_after_last_pop_, worklist_.end());
SemanticsNodeBlockId result = worklist_.pop_back_val();
size_after_last_pop_ = worklist_.size();
return result;
}
auto empty() -> bool { return worklist_.empty(); }
private:
llvm::SmallVector<SemanticsNodeBlockId> worklist_;
int size_after_last_pop_ = 0;
};
} // namespace Carbon
#endif // CARBON_TOOLCHAIN_LOWERING_LOWERING_BLOCK_WORKLIST_H_
+13 -5
View File
@@ -103,16 +103,24 @@ auto LoweringContext::BuildFunctionDefinition(SemanticsFunctionId function_id)
function_lowering.SetLocal(param_storage, llvm_function->getArg(i));
}
CARBON_VLOG() << "Lowering " << function.body_id << "\n";
for (const auto& node_id : semantics_ir().GetNodeBlock(function.body_id)) {
auto node = semantics_ir().GetNode(node_id);
CARBON_VLOG() << "Lowering " << node_id << ": " << node << "\n";
switch (node.kind()) {
// Add the entry block to the worklist.
function_lowering.GetBlock(function.body_id);
while (!function_lowering.block_worklist().empty()) {
SemanticsNodeBlockId block = function_lowering.block_worklist().Pop();
CARBON_VLOG() << "Lowering " << block << "\n";
function_lowering.builder().SetInsertPoint(
function_lowering.GetBlock(block));
for (const auto& node_id : semantics_ir().GetNodeBlock(block)) {
auto node = semantics_ir().GetNode(node_id);
CARBON_VLOG() << "Lowering " << node_id << ": " << node << "\n";
switch (node.kind()) {
#define CARBON_SEMANTICS_NODE_KIND(Name) \
case SemanticsNodeKind::Name: \
LoweringHandle##Name(function_lowering, node_id, node); \
break;
#include "toolchain/semantics/semantics_node_kind.def"
}
}
}
}
@@ -14,9 +14,54 @@ LoweringFunctionContext::LoweringFunctionContext(
LoweringContext& lowering_context, llvm::Function* function)
: lowering_context_(&lowering_context),
function_(function),
builder_(lowering_context.llvm_context()) {
builder_.SetInsertPoint(
llvm::BasicBlock::Create(llvm_context(), "entry", function_));
builder_(lowering_context.llvm_context()) {}
auto LoweringFunctionContext::GetBlock(SemanticsNodeBlockId block_id)
-> llvm::BasicBlock* {
llvm::BasicBlock*& entry = blocks_[block_id];
if (!entry) {
entry = llvm::BasicBlock::Create(llvm_context(), "", function_);
block_worklist().Push(block_id);
}
return entry;
}
auto LoweringFunctionContext::TryToReuseBlock(SemanticsNodeBlockId block_id,
llvm::BasicBlock* block) -> bool {
if (!blocks_.insert({block_id, block}).second) {
return false;
}
block_worklist().Push(block_id);
if (block == synthetic_block_) {
synthetic_block_ = nullptr;
}
return true;
}
auto LoweringFunctionContext::GetBlockArg(SemanticsNodeBlockId block_id,
SemanticsTypeId type_id)
-> llvm::PHINode* {
llvm::BasicBlock* block = GetBlock(block_id);
// Find the existing phi, if any.
auto phis = block->phis();
if (!phis.empty()) {
CARBON_CHECK(std::next(phis.begin()) == phis.end())
<< "Expected at most one phi, found "
<< std::distance(phis.begin(), phis.end());
return &*phis.begin();
}
// The number of predecessor slots to reserve.
static constexpr unsigned NumReservedPredecessors = 2;
auto* phi = llvm::PHINode::Create(GetType(type_id), NumReservedPredecessors);
phi->insertInto(block, block->begin());
return phi;
}
auto LoweringFunctionContext::CreateSyntheticBlock() -> llvm::BasicBlock* {
synthetic_block_ = llvm::BasicBlock::Create(llvm_context(), "", function_);
return synthetic_block_;
}
auto LoweringFunctionContext::GetLocalLoaded(SemanticsNodeId node_id)
@@ -5,9 +5,12 @@
#ifndef CARBON_TOOLCHAIN_LOWERING_LOWERING_FUNCTION_CONTEXT_H_
#define CARBON_TOOLCHAIN_LOWERING_LOWERING_FUNCTION_CONTEXT_H_
#include <optional>
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "toolchain/lowering/lowering_block_worklist.h"
#include "toolchain/lowering/lowering_context.h"
#include "toolchain/semantics/semantics_ir.h"
#include "toolchain/semantics/semantics_node.h"
@@ -21,6 +24,21 @@ class LoweringFunctionContext {
explicit LoweringFunctionContext(LoweringContext& lowering_context,
llvm::Function* function);
// Returns a basic block corresponding to the start of the given semantics
// block, and enqueues it for emission.
auto GetBlock(SemanticsNodeBlockId block_id) -> llvm::BasicBlock*;
// If we have not yet allocated a `BasicBlock` for this `block_id`, set it to
// `block`, and enqueue `block_id` for emission. Returns whether we set the
// block.
auto TryToReuseBlock(SemanticsNodeBlockId block_id, llvm::BasicBlock* block)
-> bool;
// Returns a phi node corresponding to the block argument of the given basic
// block.
auto GetBlockArg(SemanticsNodeBlockId block_id, SemanticsTypeId type_id)
-> llvm::PHINode*;
// Returns a local (versus global) value for the given node.
auto GetLocal(SemanticsNodeId node_id) -> llvm::Value* {
auto it = locals_.find(node_id);
@@ -48,6 +66,17 @@ class LoweringFunctionContext {
return lowering_context_->GetType(type_id);
}
// Create a synthetic block that corresponds to no SemanticsNodeBlockId. Such
// a block should only ever have a single predecessor, and is used when we
// need multiple `llvm::BasicBlock`s to model the linear control flow in a
// single SemanticsIR block.
auto CreateSyntheticBlock() -> llvm::BasicBlock*;
// Determine whether block is the most recently created synthetic block.
auto IsCurrentSyntheticBlock(llvm::BasicBlock* block) -> bool {
return synthetic_block_ == block;
}
auto llvm_context() -> llvm::LLVMContext& {
return lowering_context_->llvm_context();
}
@@ -55,6 +84,7 @@ class LoweringFunctionContext {
return lowering_context_->llvm_module();
}
auto builder() -> llvm::IRBuilder<>& { return builder_; }
auto block_worklist() -> LoweringBlockWorklist& { return block_worklist_; }
auto semantics_ir() -> const SemanticsIR& {
return lowering_context_->semantics_ir();
}
@@ -68,6 +98,16 @@ class LoweringFunctionContext {
llvm::IRBuilder<> builder_;
// A worklist for blocks we need to lower.
LoweringBlockWorklist block_worklist_;
// Maps a function's SemanticsIR blocks to lowered blocks.
llvm::DenseMap<SemanticsNodeBlockId, llvm::BasicBlock*> blocks_;
// The synthetic block we most recently created. May be null if there is no
// such block.
llvm::BasicBlock* synthetic_block_ = nullptr;
// Maps a function's SemanticsIR nodes to lowered values.
// TODO: Handle nested scopes. Right now this is just cleared at the end of
// every block.
+48 -9
View File
@@ -38,10 +38,11 @@ auto LoweringHandleBindName(LoweringFunctionContext& /*context*/,
// Probably need to do something here, but not necessary for now.
}
auto LoweringHandleBlockArg(LoweringFunctionContext& /*context*/,
SemanticsNodeId /*node_id*/, SemanticsNode node)
auto LoweringHandleBlockArg(LoweringFunctionContext& context,
SemanticsNodeId node_id, SemanticsNode node)
-> void {
CARBON_FATAL() << "TODO: Add support: " << node;
SemanticsNodeBlockId block_id = node.GetAsBlockArg();
context.SetLocal(node_id, context.GetBlockArg(block_id, node.type_id()));
}
auto LoweringHandleBoolLiteral(LoweringFunctionContext& context,
@@ -52,22 +53,60 @@ auto LoweringHandleBoolLiteral(LoweringFunctionContext& context,
context.SetLocal(node_id, v);
}
auto LoweringHandleBranch(LoweringFunctionContext& /*context*/,
auto LoweringHandleBranch(LoweringFunctionContext& context,
SemanticsNodeId /*node_id*/, SemanticsNode node)
-> void {
CARBON_FATAL() << "TODO: Add support: " << node;
SemanticsNodeBlockId target_block_id = node.GetAsBranch();
// Opportunistically avoid creating a BasicBlock that contains just a branch.
llvm::BasicBlock* block = context.builder().GetInsertBlock();
if (block->empty() && context.TryToReuseBlock(target_block_id, block)) {
// Reuse this block as the branch target.
} else {
context.builder().CreateBr(context.GetBlock(target_block_id));
}
context.builder().ClearInsertionPoint();
}
auto LoweringHandleBranchIf(LoweringFunctionContext& /*context*/,
auto LoweringHandleBranchIf(LoweringFunctionContext& context,
SemanticsNodeId /*node_id*/, SemanticsNode node)
-> void {
CARBON_FATAL() << "TODO: Add support: " << node;
auto [target_block_id, cond_id] = node.GetAsBranchIf();
llvm::Value* cond = context.GetLocalLoaded(cond_id);
llvm::BasicBlock* then_block = context.GetBlock(target_block_id);
llvm::BasicBlock* else_block = context.CreateSyntheticBlock();
context.builder().CreateCondBr(cond, then_block, else_block);
context.builder().SetInsertPoint(else_block);
}
auto LoweringHandleBranchWithArg(LoweringFunctionContext& /*context*/,
auto LoweringHandleBranchWithArg(LoweringFunctionContext& context,
SemanticsNodeId /*node_id*/,
SemanticsNode node) -> void {
CARBON_FATAL() << "TODO: Add support: " << node;
auto [target_block_id, arg_id] = node.GetAsBranchWithArg();
llvm::Value* arg = context.GetLocalLoaded(arg_id);
SemanticsTypeId arg_type_id =
context.semantics_ir().GetNode(arg_id).type_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(target_block_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(target_block_id));
}
context.GetBlockArg(target_block_id, arg_type_id)
->addIncoming(arg, phi_predecessor);
context.builder().ClearInsertionPoint();
}
auto LoweringHandleBuiltin(LoweringFunctionContext& /*context*/,
-2
View File
@@ -7,12 +7,10 @@
// CHECK:STDOUT: source_filename = "false_true.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @F() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret i1 false
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @T() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret i1 true
// CHECK:STDOUT: }
-1
View File
@@ -7,7 +7,6 @@
// CHECK:STDOUT: source_filename = "zero.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret i32 0
// CHECK:STDOUT: }
@@ -10,19 +10,17 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %StructLiteralType @Echo(%StructLiteralType %a) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %StructLiteralValue = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %0 = load %StructLiteralType, ptr %StructLiteralValue, align 1
// CHECK:STDOUT: ret %StructLiteralType %0
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %StructLiteralValue, align 1
// CHECK:STDOUT: ret %StructLiteralType %1
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %StructLiteralValue = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %var = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %StructLiteralValue1 = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %0 = load %StructLiteralType, ptr %StructLiteralValue1, align 1
// CHECK:STDOUT: %Echo = call %StructLiteralType @Echo(%StructLiteralType %0)
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %StructLiteralValue1, align 1
// CHECK:STDOUT: %Echo = call %StructLiteralType @Echo(%StructLiteralType %1)
// CHECK:STDOUT: store %StructLiteralType %Echo, ptr %var, align 1
// CHECK:STDOUT: }
-2
View File
@@ -9,12 +9,10 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Echo(i32 %a) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret i32 %a
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %var = alloca i32, align 4
// CHECK:STDOUT: %Echo = call i32 @Echo(i32 1)
// CHECK:STDOUT: store i32 %Echo, ptr %var, align 4
@@ -9,11 +9,9 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo(i32 %a) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %Foo = call %EmptyTupleType @Foo(i32 1)
// CHECK:STDOUT: }
@@ -9,11 +9,9 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo(i32 %a) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %Foo = call %EmptyTupleType @Foo(i32 1)
// CHECK:STDOUT: %Foo1 = call %EmptyTupleType @Foo(i32 1)
// CHECK:STDOUT: }
@@ -9,11 +9,9 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo(i32 %a, i32 %b) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %Foo = call %EmptyTupleType @Foo(i32 1, i32 2)
// CHECK:STDOUT: }
@@ -9,11 +9,9 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo(i32 %a, i32 %b) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %Foo = call %EmptyTupleType @Foo(i32 1, i32 2)
// CHECK:STDOUT: %Foo1 = call %EmptyTupleType @Foo(i32 1, i32 2)
// CHECK:STDOUT: }
@@ -9,11 +9,9 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %Foo = call %EmptyTupleType @Foo()
// CHECK:STDOUT: }
+2 -4
View File
@@ -9,15 +9,13 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @DoNothing(i32 %a) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %var = alloca i32, align 4
// CHECK:STDOUT: store i32 0, ptr %var, align 4
// CHECK:STDOUT: %0 = load i32, ptr %var, align 4
// CHECK:STDOUT: %DoNothing = call %EmptyTupleType @DoNothing(i32 %0)
// CHECK:STDOUT: %1 = load i32, ptr %var, align 4
// CHECK:STDOUT: %DoNothing = call %EmptyTupleType @DoNothing(i32 %1)
// CHECK:STDOUT: }
fn DoNothing(a: i32) {}
@@ -10,7 +10,6 @@
// CHECK:STDOUT: %StructLiteralType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Echo(%StructLiteralType %a) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
fn Echo(a: {}) {
@@ -9,7 +9,6 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo(i32 %a) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
fn Foo(a: i32) {}
@@ -9,7 +9,6 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo(i32 %a, i32 %b) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
fn Foo(a: i32, b: i32) {}
@@ -9,7 +9,6 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Foo() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: }
fn Foo() {}
+38
View File
@@ -0,0 +1,38 @@
// 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 = 'basic.carbon'
// CHECK:STDOUT: source_filename = "basic.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @F() {
// CHECK:STDOUT: ret i32 1
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @G() {
// CHECK:STDOUT: ret i32 2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Select(i1 %b) {
// CHECK:STDOUT: br i1 %b, label %1, label %2
// CHECK:STDOUT:
// CHECK:STDOUT: 1: ; preds = %0
// CHECK:STDOUT: %F = call i32 @F()
// CHECK:STDOUT: br label %3
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %0
// CHECK:STDOUT: %G = call i32 @G()
// CHECK:STDOUT: br label %3
// CHECK:STDOUT:
// CHECK:STDOUT: 3: ; preds = %2, %1
// CHECK:STDOUT: %4 = phi i32 [ %F, %1 ], [ %G, %2 ]
// CHECK:STDOUT: ret i32 %4
// CHECK:STDOUT: }
fn F() -> i32 { return 1; }
fn G() -> i32 { return 2; }
fn Select(b: bool) -> i32 {
return if b then F() else G();
}
@@ -0,0 +1,45 @@
// 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 = 'empty_block.carbon'
// CHECK:STDOUT: source_filename = "empty_block.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Select(i1 %b, i1 %c, i1 %d) {
// CHECK:STDOUT: br i1 %b, label %1, label %2
// CHECK:STDOUT:
// CHECK:STDOUT: 1: ; preds = %0
// CHECK:STDOUT: br i1 %c, label %3, label %4
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %0
// CHECK:STDOUT: br i1 %d, label %9, label %10
// CHECK:STDOUT:
// CHECK:STDOUT: 3: ; preds = %1
// CHECK:STDOUT: br label %5
// CHECK:STDOUT:
// CHECK:STDOUT: 4: ; preds = %1
// CHECK:STDOUT: br label %5
// CHECK:STDOUT:
// CHECK:STDOUT: 5: ; preds = %4, %3
// CHECK:STDOUT: %6 = phi i32 [ 1, %3 ], [ 2, %4 ]
// CHECK:STDOUT: br label %7
// CHECK:STDOUT:
// CHECK:STDOUT: 7: ; preds = %11, %5
// CHECK:STDOUT: %8 = phi i32 [ %6, %5 ], [ %12, %11 ]
// CHECK:STDOUT: ret i32 %8
// CHECK:STDOUT:
// CHECK:STDOUT: 9: ; preds = %2
// CHECK:STDOUT: br label %11
// CHECK:STDOUT:
// CHECK:STDOUT: 10: ; preds = %2
// CHECK:STDOUT: br label %11
// CHECK:STDOUT:
// CHECK:STDOUT: 11: ; preds = %10, %9
// CHECK:STDOUT: %12 = phi i32 [ 3, %9 ], [ 4, %10 ]
// CHECK:STDOUT: br label %7
// CHECK:STDOUT: }
fn Select(b: bool, c: bool, d: bool) -> i32 {
return if b then if c then 1 else 2 else if d then 3 else 4;
}
+35
View File
@@ -0,0 +1,35 @@
// 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 = 'and.carbon'
// CHECK:STDOUT: source_filename = "and.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @F() {
// CHECK:STDOUT: ret i1 true
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @G() {
// CHECK:STDOUT: ret i1 true
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @And() {
// CHECK:STDOUT: %F = call i1 @F()
// CHECK:STDOUT: br i1 %F, label %1, label %2
// CHECK:STDOUT:
// CHECK:STDOUT: 1: ; preds = %0
// CHECK:STDOUT: %G = call i1 @G()
// CHECK:STDOUT: br label %2
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %1, %0
// CHECK:STDOUT: %3 = phi i1 [ false, %0 ], [ %G, %1 ]
// CHECK:STDOUT: ret i1 %3
// CHECK:STDOUT: }
fn F() -> bool { return true; }
fn G() -> bool { return true; }
fn And() -> bool {
return F() and G();
}
@@ -0,0 +1,24 @@
// 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 = 'and_empty_block.carbon'
// CHECK:STDOUT: source_filename = "and_empty_block.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @And(i1 %b, i1 %c) {
// CHECK:STDOUT: br i1 %b, label %1, label %2
// CHECK:STDOUT:
// CHECK:STDOUT: 1: ; preds = %0
// CHECK:STDOUT: br label %2
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %1, %0
// CHECK:STDOUT: %3 = phi i1 [ false, %0 ], [ %c, %1 ]
// CHECK:STDOUT: ret i1 %3
// CHECK:STDOUT: }
fn And(b: bool, c: bool) -> bool {
// Note that in this case, we must generate an empty block so that the `phi`
// has two distinct predecessors.
return b and c;
}
+2 -3
View File
@@ -7,9 +7,8 @@
// CHECK:STDOUT: source_filename = "not.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @Not(i1 %b) {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %0 = xor i1 %b, true
// CHECK:STDOUT: ret i1 %0
// CHECK:STDOUT: %1 = xor i1 %b, true
// CHECK:STDOUT: ret i1 %1
// CHECK:STDOUT: }
fn Not(b: bool) -> bool {
+36
View File
@@ -0,0 +1,36 @@
// 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 = 'or.carbon'
// CHECK:STDOUT: source_filename = "or.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @F() {
// CHECK:STDOUT: ret i1 true
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @G() {
// CHECK:STDOUT: ret i1 true
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @Or() {
// CHECK:STDOUT: %F = call i1 @F()
// CHECK:STDOUT: %1 = xor i1 %F, true
// CHECK:STDOUT: br i1 %1, label %2, label %3
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %0
// CHECK:STDOUT: %G = call i1 @G()
// CHECK:STDOUT: br label %3
// CHECK:STDOUT:
// CHECK:STDOUT: 3: ; preds = %2, %0
// CHECK:STDOUT: %4 = phi i1 [ true, %0 ], [ %G, %2 ]
// CHECK:STDOUT: ret i1 %4
// CHECK:STDOUT: }
fn F() -> bool { return true; }
fn G() -> bool { return true; }
fn Or() -> bool {
return F() or G();
}
@@ -0,0 +1,25 @@
// 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 = 'or_empty_block.carbon'
// CHECK:STDOUT: source_filename = "or_empty_block.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i1 @Or(i1 %b, i1 %c) {
// CHECK:STDOUT: %1 = xor i1 %b, true
// CHECK:STDOUT: br i1 %1, label %2, label %3
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %0
// CHECK:STDOUT: br label %3
// CHECK:STDOUT:
// CHECK:STDOUT: 3: ; preds = %2, %0
// CHECK:STDOUT: %4 = phi i1 [ true, %0 ], [ %c, %2 ]
// CHECK:STDOUT: ret i1 %4
// CHECK:STDOUT: }
fn Or(b: bool, c: bool) -> bool {
// Note that in this case, we must generate an empty block so that the `phi`
// has two distinct predecessors.
return b or c;
}
-1
View File
@@ -9,7 +9,6 @@
// CHECK:STDOUT: %EmptyTupleType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define %EmptyTupleType @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
-1
View File
@@ -7,7 +7,6 @@
// CHECK:STDOUT: source_filename = "value.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret i32 0
// CHECK:STDOUT: }
+2 -3
View File
@@ -7,11 +7,10 @@
// CHECK:STDOUT: source_filename = "var.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Main() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %var = alloca i32, align 4
// CHECK:STDOUT: store i32 0, ptr %var, align 4
// CHECK:STDOUT: %0 = load i32, ptr %var, align 4
// CHECK:STDOUT: ret i32 %0
// CHECK:STDOUT: %1 = load i32, ptr %var, align 4
// CHECK:STDOUT: ret i32 %1
// CHECK:STDOUT: }
fn Main() -> i32 {
+4 -5
View File
@@ -9,16 +9,15 @@
// CHECK:STDOUT: %StructLiteralType = type {}
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Run() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %StructLiteralValue = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %var = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %StructLiteralValue1 = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %0 = load %StructLiteralType, ptr %StructLiteralValue1, align 1
// CHECK:STDOUT: store %StructLiteralType %0, ptr %var, align 1
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %StructLiteralValue1, align 1
// CHECK:STDOUT: store %StructLiteralType %1, ptr %var, align 1
// CHECK:STDOUT: %StructLiteralValue2 = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %var3 = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %var, align 1
// CHECK:STDOUT: store %StructLiteralType %1, ptr %var3, align 1
// CHECK:STDOUT: %2 = load %StructLiteralType, ptr %var, align 1
// CHECK:STDOUT: store %StructLiteralType %2, ptr %var3, align 1
// CHECK:STDOUT: ret i32 0
// CHECK:STDOUT: }
+6 -7
View File
@@ -9,22 +9,21 @@
// CHECK:STDOUT: %StructLiteralType = type { double, i32 }
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Run() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %var = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %StructLiteralValue = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %a = getelementptr inbounds %StructLiteralType, ptr %StructLiteralValue, i32 0, i32 0
// CHECK:STDOUT: store double 0.000000e+00, ptr %a, align 8
// CHECK:STDOUT: %b = getelementptr inbounds %StructLiteralType, ptr %StructLiteralValue, i32 0, i32 1
// CHECK:STDOUT: store i32 1, ptr %b, align 4
// CHECK:STDOUT: %0 = load %StructLiteralType, ptr %StructLiteralValue, align 8
// CHECK:STDOUT: store %StructLiteralType %0, ptr %var, align 8
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %StructLiteralValue, align 8
// CHECK:STDOUT: store %StructLiteralType %1, ptr %var, align 8
// CHECK:STDOUT: %var1 = alloca i32, align 4
// CHECK:STDOUT: %b2 = getelementptr inbounds %StructLiteralType, ptr %var, i32 0, i32 1
// CHECK:STDOUT: %1 = load i32, ptr %b2, align 4
// CHECK:STDOUT: store i32 %1, ptr %var1, align 4
// CHECK:STDOUT: %2 = load i32, ptr %b2, align 4
// CHECK:STDOUT: store i32 %2, ptr %var1, align 4
// CHECK:STDOUT: %var3 = alloca i32, align 4
// CHECK:STDOUT: %2 = load i32, ptr %var1, align 4
// CHECK:STDOUT: store i32 %2, ptr %var3, align 4
// CHECK:STDOUT: %3 = load i32, ptr %var1, align 4
// CHECK:STDOUT: store i32 %3, ptr %var3, align 4
// CHECK:STDOUT: ret i32 0
// CHECK:STDOUT: }
+4 -5
View File
@@ -9,16 +9,15 @@
// CHECK:STDOUT: %StructLiteralType = type { i32 }
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Run() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %var = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %StructLiteralValue = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %a = getelementptr inbounds %StructLiteralType, ptr %StructLiteralValue, i32 0, i32 0
// CHECK:STDOUT: store i32 4, ptr %a, align 4
// CHECK:STDOUT: %0 = load %StructLiteralType, ptr %StructLiteralValue, align 4
// CHECK:STDOUT: store %StructLiteralType %0, ptr %var, align 4
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %StructLiteralValue, align 4
// CHECK:STDOUT: store %StructLiteralType %1, ptr %var, align 4
// CHECK:STDOUT: %var1 = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %var, align 4
// CHECK:STDOUT: store %StructLiteralType %1, ptr %var1, align 4
// CHECK:STDOUT: %2 = load %StructLiteralType, ptr %var, align 4
// CHECK:STDOUT: store %StructLiteralType %2, ptr %var1, align 4
// CHECK:STDOUT: ret i32 0
// CHECK:STDOUT: }
+4 -5
View File
@@ -9,18 +9,17 @@
// CHECK:STDOUT: %StructLiteralType = type { i32, i32 }
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Run() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %var = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %StructLiteralValue = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %a = getelementptr inbounds %StructLiteralType, ptr %StructLiteralValue, i32 0, i32 0
// CHECK:STDOUT: store i32 1, ptr %a, align 4
// CHECK:STDOUT: %b = getelementptr inbounds %StructLiteralType, ptr %StructLiteralValue, i32 0, i32 1
// CHECK:STDOUT: store i32 2, ptr %b, align 4
// CHECK:STDOUT: %0 = load %StructLiteralType, ptr %StructLiteralValue, align 4
// CHECK:STDOUT: store %StructLiteralType %0, ptr %var, align 4
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %StructLiteralValue, align 4
// CHECK:STDOUT: store %StructLiteralType %1, ptr %var, align 4
// CHECK:STDOUT: %var1 = alloca %StructLiteralType, align 8
// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %var, align 4
// CHECK:STDOUT: store %StructLiteralType %1, ptr %var1, align 4
// CHECK:STDOUT: %2 = load %StructLiteralType, ptr %var, align 4
// CHECK:STDOUT: store %StructLiteralType %2, ptr %var1, align 4
// CHECK:STDOUT: ret i32 0
// CHECK:STDOUT: }
+2 -3
View File
@@ -7,11 +7,10 @@
// CHECK:STDOUT: source_filename = "local.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define i32 @Run() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %var = alloca i32, align 4
// CHECK:STDOUT: store i32 1, ptr %var, align 4
// CHECK:STDOUT: %0 = load i32, ptr %var, align 4
// CHECK:STDOUT: ret i32 %0
// CHECK:STDOUT: %1 = load i32, ptr %var, align 4
// CHECK:STDOUT: ret i32 %1
// CHECK:STDOUT: }
fn Run() -> i32 {
+2 -1
View File
@@ -228,7 +228,8 @@ auto SemanticsHandleInfixOperator(SemanticsContext& context,
context.AddNodeAndPush(
parse_node,
SemanticsNode::BlockArg::Make(
parse_node, context.semantics_ir().GetNode(rhs_id).type_id()));
parse_node, context.semantics_ir().GetNode(rhs_id).type_id(),
resume_block_id));
break;
}
@@ -68,7 +68,8 @@ auto SemanticsHandleIfExpressionElse(SemanticsContext& context,
// Obtain the value in the resumption block and push it.
context.AddNodeAndPush(
if_node, SemanticsNode::BlockArg::Make(if_node, result_type_id));
if_node,
SemanticsNode::BlockArg::Make(if_node, result_type_id, resume_block_id));
return true;
}
+5 -1
View File
@@ -285,7 +285,8 @@ class SemanticsNode {
SemanticsStringId /*name_id*/,
SemanticsNodeId /*node_id*/>;
using BlockArg = Factory<SemanticsNodeKind::BlockArg>;
using BlockArg =
Factory<SemanticsNodeKind::BlockArg, SemanticsNodeBlockId /*block_id*/>;
using BoolLiteral =
Factory<SemanticsNodeKind::BoolLiteral, SemanticsBoolValue /*value*/>;
@@ -450,6 +451,9 @@ struct SemanticsIdMapInfo {
// Support use of Id types as DenseMap/DenseSet keys.
template <>
struct llvm::DenseMapInfo<Carbon::SemanticsNodeBlockId>
: public Carbon::SemanticsIdMapInfo<Carbon::SemanticsNodeBlockId> {};
template <>
struct llvm::DenseMapInfo<Carbon::SemanticsNodeId>
: public Carbon::SemanticsIdMapInfo<Carbon::SemanticsNodeId> {};
template <>
+1 -1
View File
@@ -35,7 +35,7 @@
// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: node+4, arg1: node+2, type: type1},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block7, arg1: node+9},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block7, arg1: node+10},
// CHECK:STDOUT: {kind: BlockArg, type: type1},
// CHECK:STDOUT: {kind: BlockArg, arg0: block7, type: type1},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13, type: type1},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
@@ -41,7 +41,7 @@
// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block7, arg1: node+10},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block7, arg1: node+11},
// CHECK:STDOUT: {kind: BlockArg, type: type0},
// CHECK:STDOUT: {kind: BlockArg, arg0: block7, type: type0},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+14, type: type0},
// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function3},
// CHECK:STDOUT: {kind: BoolLiteral, arg0: false, type: type1},
@@ -51,7 +51,7 @@
// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block11, arg1: node+20},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block11, arg1: node+21},
// CHECK:STDOUT: {kind: BlockArg, type: type0},
// CHECK:STDOUT: {kind: BlockArg, arg0: block11, type: type0},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+24, type: type0},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
@@ -41,7 +41,7 @@
// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+11},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+12},
// CHECK:STDOUT: {kind: BlockArg, type: type0},
// CHECK:STDOUT: {kind: BlockArg, arg0: block9, type: type0},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+15, type: type0},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
+3 -3
View File
@@ -41,17 +41,17 @@
// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type1},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+11},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+12},
// CHECK:STDOUT: {kind: BlockArg, type: type1},
// CHECK:STDOUT: {kind: BlockArg, arg0: block9, type: type1},
// CHECK:STDOUT: {kind: BranchIf, arg0: block11, arg1: node+4},
// CHECK:STDOUT: {kind: Branch, arg0: block10},
// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type1},
// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int3, type: type1},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block12, arg1: node+18},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block12, arg1: node+19},
// CHECK:STDOUT: {kind: BlockArg, type: type1},
// CHECK:STDOUT: {kind: BlockArg, arg0: block12, type: type1},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block13, arg1: node+15},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block13, arg1: node+22},
// CHECK:STDOUT: {kind: BlockArg, type: type1},
// CHECK:STDOUT: {kind: BlockArg, arg0: block13, type: type1},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+25, type: type1},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
+1 -1
View File
@@ -35,7 +35,7 @@
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+8},
// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+11},
// CHECK:STDOUT: {kind: BlockArg, type: type0},
// CHECK:STDOUT: {kind: BlockArg, arg0: block5, type: type0},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13, type: type0},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
+1 -1
View File
@@ -36,7 +36,7 @@
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+9},
// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0},
// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+12},
// CHECK:STDOUT: {kind: BlockArg, type: type0},
// CHECK:STDOUT: {kind: BlockArg, arg0: block5, type: type0},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+14, type: type0},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [