mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 07:51:05 +01:00
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:
co-authored by
Jon Ross-Perkins
parent
c27f41c94b
commit
0d4d392d12
@@ -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_
|
||||
@@ -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.
|
||||
|
||||
@@ -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*/,
|
||||
|
||||
@@ -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: }
|
||||
|
||||
|
||||
@@ -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: }
|
||||
|
||||
|
||||
@@ -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: }
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
// CHECK:STDOUT: %EmptyTupleType = type {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define %EmptyTupleType @Main() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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: [
|
||||
|
||||
@@ -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
@@ -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
@@ -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: [
|
||||
|
||||
Reference in New Issue
Block a user