Support for while statements. (#3258)

Basic support for type-checking and lowering of `while` statements, as
well as `break` and `continue`.
This commit is contained in:
Richard Smith
2023-10-11 19:20:46 +00:00
committed by GitHub
parent c7a9e29a89
commit 6e9d83e746
16 changed files with 710 additions and 12 deletions
+13
View File
@@ -21,6 +21,12 @@ namespace Carbon::Check {
// Context and shared functionality for semantics handlers.
class Context {
public:
// A scope in which `break` and `continue` can be used.
struct BreakContinueScope {
SemIR::NodeBlockId break_target;
SemIR::NodeBlockId continue_target;
};
// Stores references for work.
explicit Context(const Lex::TokenizedBuffer& tokens,
DiagnosticEmitter<Parse::Node>& emitter,
@@ -186,6 +192,10 @@ class Context {
return return_scope_stack_;
}
auto break_continue_stack() -> llvm::SmallVector<BreakContinueScope>& {
return break_continue_stack_;
}
auto declaration_name_stack() -> DeclarationNameStack& {
return declaration_name_stack_;
}
@@ -273,6 +283,9 @@ class Context {
// this will be a FunctionDeclaration.
llvm::SmallVector<SemIR::NodeId> return_scope_stack_;
// A stack of `break` and `continue` targets.
llvm::SmallVector<BreakContinueScope> break_continue_stack_;
// A stack for scope context.
llvm::SmallVector<ScopeStackEntry> scope_stack_;
+79 -12
View File
@@ -3,25 +3,50 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
namespace Carbon::Check {
auto HandleBreakStatement(Context& context, Parse::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleBreakStatement");
auto HandleBreakStatement(Context& /*context*/, Parse::Node /*parse_node*/)
-> bool {
return true;
}
auto HandleBreakStatementStart(Context& context, Parse::Node parse_node)
-> bool {
return context.TODO(parse_node, "HandleBreakStatementStart");
auto& stack = context.break_continue_stack();
if (stack.empty()) {
CARBON_DIAGNOSTIC(BreakOutsideLoop, Error,
"`break` can only be used in a loop.");
context.emitter().Emit(parse_node, BreakOutsideLoop);
} else {
context.AddNode(SemIR::Branch(parse_node, stack.back().break_target));
}
context.node_block_stack().Pop();
context.node_block_stack().PushUnreachable();
return true;
}
auto HandleContinueStatement(Context& context, Parse::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleContinueStatement");
auto HandleContinueStatement(Context& /*context*/, Parse::Node /*parse_node*/)
-> bool {
return true;
}
auto HandleContinueStatementStart(Context& context, Parse::Node parse_node)
-> bool {
return context.TODO(parse_node, "HandleContinueStatementStart");
auto& stack = context.break_continue_stack();
if (stack.empty()) {
CARBON_DIAGNOSTIC(ContinueOutsideLoop, Error,
"`continue` can only be used in a loop.");
context.emitter().Emit(parse_node, ContinueOutsideLoop);
} else {
context.AddNode(SemIR::Branch(parse_node, stack.back().continue_target));
}
context.node_block_stack().Pop();
context.node_block_stack().PushUnreachable();
return true;
}
auto HandleForHeader(Context& context, Parse::Node parse_node) -> bool {
@@ -40,17 +65,59 @@ auto HandleForStatement(Context& context, Parse::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleForStatement");
}
auto HandleWhileCondition(Context& context, Parse::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleWhileCondition");
}
auto HandleWhileConditionStart(Context& context, Parse::Node parse_node)
-> bool {
return context.TODO(parse_node, "HandleWhileConditionStart");
// Branch to the loop header block. Note that we create a new block here even
// if the current block is empty; this ensures that the loop always has a
// preheader block.
auto loop_header_id = context.AddDominatedBlockAndBranch(parse_node);
context.node_block_stack().Pop();
// Start emitting the loop header block.
context.node_block_stack().Push(loop_header_id);
context.AddCurrentCodeBlockToFunction();
context.node_stack().Push(parse_node, loop_header_id);
return true;
}
auto HandleWhileCondition(Context& context, Parse::Node parse_node) -> bool {
auto cond_value_id = context.node_stack().PopExpression();
auto loop_header_id =
context.node_stack().Peek<Parse::NodeKind::WhileConditionStart>();
cond_value_id = ConvertToBoolValue(context, parse_node, cond_value_id);
// Branch to either the loop body or the loop exit block.
auto loop_body_id =
context.AddDominatedBlockAndBranchIf(parse_node, cond_value_id);
auto loop_exit_id = context.AddDominatedBlockAndBranch(parse_node);
context.node_block_stack().Pop();
// Start emitting the loop body.
context.node_block_stack().Push(loop_body_id);
context.AddCurrentCodeBlockToFunction();
context.break_continue_stack().push_back(
{.break_target = loop_exit_id, .continue_target = loop_header_id});
context.node_stack().Push(parse_node, loop_exit_id);
return true;
}
auto HandleWhileStatement(Context& context, Parse::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleWhileStatement");
auto loop_exit_id =
context.node_stack().Pop<Parse::NodeKind::WhileCondition>();
auto loop_header_id =
context.node_stack().Pop<Parse::NodeKind::WhileConditionStart>();
context.break_continue_stack().pop_back();
// Add the loop backedge.
context.AddNode(SemIR::Branch(parse_node, loop_header_id));
context.node_block_stack().Pop();
// Start emitting the loop exit block.
context.node_block_stack().Push(loop_exit_id);
context.AddCurrentCodeBlockToFunction();
return true;
}
} // namespace Carbon::Check
+2
View File
@@ -264,6 +264,8 @@ class NodeStack {
case Parse::NodeKind::IfCondition:
case Parse::NodeKind::IfExpressionIf:
case Parse::NodeKind::ParameterList:
case Parse::NodeKind::WhileCondition:
case Parse::NodeKind::WhileConditionStart:
return IdKind::NodeBlockId;
case Parse::NodeKind::FunctionDefinitionStart:
return IdKind::FunctionId;
+154
View File
@@ -0,0 +1,154 @@
// 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
fn A() -> bool;
fn B() -> bool;
fn C() -> bool;
fn D() -> bool;
fn E() -> bool;
fn F() -> bool;
fn G() -> bool;
fn H() -> bool;
fn While() {
while (A()) {
if (B()) { continue; }
if (C()) { break; }
while (D()) {
if (E()) { continue; }
if (F()) { break; }
}
if (G()) { continue; }
if (H()) { break; }
}
}
// CHECK:STDOUT: file "break_continue.carbon" {
// CHECK:STDOUT: %A = fn_decl @A
// CHECK:STDOUT: %B = fn_decl @B
// CHECK:STDOUT: %C = fn_decl @C
// CHECK:STDOUT: %D = fn_decl @D
// CHECK:STDOUT: %E = fn_decl @E
// CHECK:STDOUT: %F = fn_decl @F
// CHECK:STDOUT: %G = fn_decl @G
// CHECK:STDOUT: %H = fn_decl @H
// CHECK:STDOUT: %While = fn_decl @While
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @A() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @B() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @C() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @D() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @H() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @While() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: br !while.cond.loc17
// CHECK:STDOUT:
// CHECK:STDOUT: !while.cond.loc17:
// CHECK:STDOUT: %A.ref = name_reference_untyped "A", package.%A
// CHECK:STDOUT: %.loc17_11.1: init bool = call @A()
// CHECK:STDOUT: %.loc17_11.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc17_11.3: ref bool = temporary %.loc17_11.2, %.loc17_11.1
// CHECK:STDOUT: %.loc17_11.4: bool = bind_value %.loc17_11.3
// CHECK:STDOUT: if %.loc17_11.4 br !while.body.loc17 else br !while.done.loc17
// CHECK:STDOUT:
// CHECK:STDOUT: !while.body.loc17:
// CHECK:STDOUT: %B.ref = name_reference_untyped "B", package.%B
// CHECK:STDOUT: %.loc18_10.1: init bool = call @B()
// CHECK:STDOUT: %.loc18_10.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc18_10.3: ref bool = temporary %.loc18_10.2, %.loc18_10.1
// CHECK:STDOUT: %.loc18_10.4: bool = bind_value %.loc18_10.3
// CHECK:STDOUT: if %.loc18_10.4 br !if.then.loc18 else br !if.else.loc18
// CHECK:STDOUT:
// CHECK:STDOUT: !if.then.loc18:
// CHECK:STDOUT: br !while.cond.loc17
// CHECK:STDOUT:
// CHECK:STDOUT: !if.else.loc18:
// CHECK:STDOUT: %C.ref = name_reference_untyped "C", package.%C
// CHECK:STDOUT: %.loc19_10.1: init bool = call @C()
// CHECK:STDOUT: %.loc19_10.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc19_10.3: ref bool = temporary %.loc19_10.2, %.loc19_10.1
// CHECK:STDOUT: %.loc19_10.4: bool = bind_value %.loc19_10.3
// CHECK:STDOUT: if %.loc19_10.4 br !if.then.loc19 else br !if.else.loc19
// CHECK:STDOUT:
// CHECK:STDOUT: !if.then.loc19:
// CHECK:STDOUT: br !while.done.loc17
// CHECK:STDOUT:
// CHECK:STDOUT: !if.else.loc19:
// CHECK:STDOUT: br !while.cond.loc20
// CHECK:STDOUT:
// CHECK:STDOUT: !while.cond.loc20:
// CHECK:STDOUT: %D.ref = name_reference_untyped "D", package.%D
// CHECK:STDOUT: %.loc20_13.1: init bool = call @D()
// CHECK:STDOUT: %.loc20_13.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc20_13.3: ref bool = temporary %.loc20_13.2, %.loc20_13.1
// CHECK:STDOUT: %.loc20_13.4: bool = bind_value %.loc20_13.3
// CHECK:STDOUT: if %.loc20_13.4 br !while.body.loc20 else br !while.done.loc20
// CHECK:STDOUT:
// CHECK:STDOUT: !while.body.loc20:
// CHECK:STDOUT: %E.ref = name_reference_untyped "E", package.%E
// CHECK:STDOUT: %.loc21_12.1: init bool = call @E()
// CHECK:STDOUT: %.loc21_12.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc21_12.3: ref bool = temporary %.loc21_12.2, %.loc21_12.1
// CHECK:STDOUT: %.loc21_12.4: bool = bind_value %.loc21_12.3
// CHECK:STDOUT: if %.loc21_12.4 br !if.then.loc21 else br !if.else.loc21
// CHECK:STDOUT:
// CHECK:STDOUT: !if.then.loc21:
// CHECK:STDOUT: br !while.cond.loc20
// CHECK:STDOUT:
// CHECK:STDOUT: !if.else.loc21:
// CHECK:STDOUT: %F.ref = name_reference_untyped "F", package.%F
// CHECK:STDOUT: %.loc22_12.1: init bool = call @F()
// CHECK:STDOUT: %.loc22_12.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc22_12.3: ref bool = temporary %.loc22_12.2, %.loc22_12.1
// CHECK:STDOUT: %.loc22_12.4: bool = bind_value %.loc22_12.3
// CHECK:STDOUT: if %.loc22_12.4 br !if.then.loc22 else br !if.else.loc22
// CHECK:STDOUT:
// CHECK:STDOUT: !if.then.loc22:
// CHECK:STDOUT: br !while.done.loc20
// CHECK:STDOUT:
// CHECK:STDOUT: !if.else.loc22:
// CHECK:STDOUT: br !while.cond.loc20
// CHECK:STDOUT:
// CHECK:STDOUT: !while.done.loc20:
// CHECK:STDOUT: %G.ref = name_reference_untyped "G", package.%G
// CHECK:STDOUT: %.loc24_10.1: init bool = call @G()
// CHECK:STDOUT: %.loc24_10.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc24_10.3: ref bool = temporary %.loc24_10.2, %.loc24_10.1
// CHECK:STDOUT: %.loc24_10.4: bool = bind_value %.loc24_10.3
// CHECK:STDOUT: if %.loc24_10.4 br !if.then.loc24 else br !if.else.loc24
// CHECK:STDOUT:
// CHECK:STDOUT: !if.then.loc24:
// CHECK:STDOUT: br !while.cond.loc17
// CHECK:STDOUT:
// CHECK:STDOUT: !if.else.loc24:
// CHECK:STDOUT: %H.ref = name_reference_untyped "H", package.%H
// CHECK:STDOUT: %.loc25_10.1: init bool = call @H()
// CHECK:STDOUT: %.loc25_10.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc25_10.3: ref bool = temporary %.loc25_10.2, %.loc25_10.1
// CHECK:STDOUT: %.loc25_10.4: bool = bind_value %.loc25_10.3
// CHECK:STDOUT: if %.loc25_10.4 br !if.then.loc25 else br !if.else.loc25
// CHECK:STDOUT:
// CHECK:STDOUT: !if.then.loc25:
// CHECK:STDOUT: br !while.done.loc17
// CHECK:STDOUT:
// CHECK:STDOUT: !if.else.loc25:
// CHECK:STDOUT: br !while.cond.loc17
// CHECK:STDOUT:
// CHECK:STDOUT: !while.done.loc17:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
@@ -0,0 +1,31 @@
// 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
fn While() {
// CHECK:STDERR: fail_bad_condition.carbon:[[@LINE+3]]:17: ERROR: Cannot implicitly convert from `String` to `bool`.
// CHECK:STDERR: while ("Hello") {}
// CHECK:STDERR: ^
while ("Hello") {}
}
// CHECK:STDOUT: file "fail_bad_condition.carbon" {
// CHECK:STDOUT: %While = fn_decl @While
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @While() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: br !while.cond
// CHECK:STDOUT:
// CHECK:STDOUT: !while.cond:
// CHECK:STDOUT: %.loc11: String = string_literal "Hello"
// CHECK:STDOUT: if <error> br !while.body else br !while.done
// CHECK:STDOUT:
// CHECK:STDOUT: !while.body:
// CHECK:STDOUT: br !while.cond
// CHECK:STDOUT:
// CHECK:STDOUT: !while.done:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
@@ -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
fn While() {
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `continue` can only be used in a loop.
// CHECK:STDERR: continue;
// CHECK:STDERR: ^
continue;
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `break` can only be used in a loop.
// CHECK:STDERR: break;
// CHECK:STDERR: ^
break;
while (false) {
continue;
break;
}
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `continue` can only be used in a loop.
// CHECK:STDERR: continue;
// CHECK:STDERR: ^
continue;
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `break` can only be used in a loop.
// CHECK:STDERR: break;
// CHECK:STDERR: ^
break;
}
// CHECK:STDOUT: file "fail_break_continue.carbon" {
// CHECK:STDOUT: %While = fn_decl @While
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @While() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: }
+62
View File
@@ -0,0 +1,62 @@
// 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
fn Cond() -> bool;
fn F();
fn G();
fn H();
fn While() {
F();
while (Cond()) {
G();
return;
}
H();
}
// CHECK:STDOUT: file "unreachable_end.carbon" {
// CHECK:STDOUT: %Cond = fn_decl @Cond
// CHECK:STDOUT: %F = fn_decl @F
// CHECK:STDOUT: %G = fn_decl @G
// CHECK:STDOUT: %H = fn_decl @H
// CHECK:STDOUT: %While = fn_decl @While
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Cond() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @H();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @While() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %F.ref = name_reference_untyped "F", package.%F
// CHECK:STDOUT: %.loc14_4.1: type = tuple_type ()
// CHECK:STDOUT: %.loc14_4.2: init () = call @F()
// CHECK:STDOUT: br !while.cond
// CHECK:STDOUT:
// CHECK:STDOUT: !while.cond:
// CHECK:STDOUT: %Cond.ref = name_reference_untyped "Cond", package.%Cond
// CHECK:STDOUT: %.loc15_14.1: init bool = call @Cond()
// CHECK:STDOUT: %.loc15_14.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc15_14.3: ref bool = temporary %.loc15_14.2, %.loc15_14.1
// CHECK:STDOUT: %.loc15_14.4: bool = bind_value %.loc15_14.3
// CHECK:STDOUT: if %.loc15_14.4 br !while.body else br !while.done
// CHECK:STDOUT:
// CHECK:STDOUT: !while.body:
// CHECK:STDOUT: %G.ref = name_reference_untyped "G", package.%G
// CHECK:STDOUT: %.loc16: init () = call @G()
// CHECK:STDOUT: return
// CHECK:STDOUT:
// CHECK:STDOUT: !while.done:
// CHECK:STDOUT: %H.ref = name_reference_untyped "H", package.%H
// CHECK:STDOUT: %.loc19: init () = call @H()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
+61
View File
@@ -0,0 +1,61 @@
// 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
fn Cond() -> bool;
fn F();
fn G();
fn H();
fn While() {
F();
while (Cond()) {
G();
}
H();
}
// CHECK:STDOUT: file "while.carbon" {
// CHECK:STDOUT: %Cond = fn_decl @Cond
// CHECK:STDOUT: %F = fn_decl @F
// CHECK:STDOUT: %G = fn_decl @G
// CHECK:STDOUT: %H = fn_decl @H
// CHECK:STDOUT: %While = fn_decl @While
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Cond() -> bool;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @H();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @While() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %F.ref = name_reference_untyped "F", package.%F
// CHECK:STDOUT: %.loc14_4.1: type = tuple_type ()
// CHECK:STDOUT: %.loc14_4.2: init () = call @F()
// CHECK:STDOUT: br !while.cond
// CHECK:STDOUT:
// CHECK:STDOUT: !while.cond:
// CHECK:STDOUT: %Cond.ref = name_reference_untyped "Cond", package.%Cond
// CHECK:STDOUT: %.loc15_14.1: init bool = call @Cond()
// CHECK:STDOUT: %.loc15_14.2: ref bool = temporary_storage
// CHECK:STDOUT: %.loc15_14.3: ref bool = temporary %.loc15_14.2, %.loc15_14.1
// CHECK:STDOUT: %.loc15_14.4: bool = bind_value %.loc15_14.3
// CHECK:STDOUT: if %.loc15_14.4 br !while.body else br !while.done
// CHECK:STDOUT:
// CHECK:STDOUT: !while.body:
// CHECK:STDOUT: %G.ref = name_reference_untyped "G", package.%G
// CHECK:STDOUT: %.loc16: init () = call @G()
// CHECK:STDOUT: br !while.cond
// CHECK:STDOUT:
// CHECK:STDOUT: !while.done:
// CHECK:STDOUT: %H.ref = name_reference_untyped "H", package.%H
// CHECK:STDOUT: %.loc18: init () = call @H()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
@@ -114,6 +114,8 @@ CARBON_DIAGNOSTIC_KIND(AddressOfNonReference)
CARBON_DIAGNOSTIC_KIND(ArrayInitFromLiteralArgCountMismatch)
CARBON_DIAGNOSTIC_KIND(ArrayInitFromExpressionArgCountMismatch)
CARBON_DIAGNOSTIC_KIND(AssignmentToNonAssignable)
CARBON_DIAGNOSTIC_KIND(BreakOutsideLoop)
CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop)
CARBON_DIAGNOSTIC_KIND(DereferenceOfNonPointer)
CARBON_DIAGNOSTIC_KIND(DereferenceOfType)
CARBON_DIAGNOSTIC_KIND(NameNotFound)
+8
View File
@@ -186,6 +186,14 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
function_lowering.builder().SetInsertPoint(llvm_block);
function_lowering.LowerBlock(block_id);
}
// LLVM requires that the entry block has no predecessors.
auto* entry_block = &llvm_function->getEntryBlock();
if (entry_block->hasNPredecessorsOrMore(1)) {
auto* new_entry_block = llvm::BasicBlock::Create(
llvm_context(), "entry", llvm_function, entry_block);
llvm::BranchInst::Create(entry_block, new_entry_block);
}
}
auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
+1
View File
@@ -74,6 +74,7 @@ auto HandleBoolLiteral(FunctionContext& context, SemIR::NodeId node_id,
auto HandleBranch(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::Branch node) -> void {
// Opportunistically avoid creating a BasicBlock that contains just a branch.
// TODO: Don't do this if it would remove a loop preheader block.
llvm::BasicBlock* block = context.builder().GetInsertBlock();
if (block->empty() && context.TryToReuseBlock(node.target_id, block)) {
// Reuse this block as the branch target.
+66
View File
@@ -0,0 +1,66 @@
// 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
fn A() -> bool;
fn B() -> bool;
fn C() -> bool;
fn While() {
while (A()) {
if (B()) { continue; }
if (C()) { break; }
}
}
// CHECK:STDOUT: ; ModuleID = 'break_continue.carbon'
// CHECK:STDOUT: source_filename = "break_continue.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: declare i1 @A()
// CHECK:STDOUT:
// CHECK:STDOUT: declare i1 @B()
// CHECK:STDOUT:
// CHECK:STDOUT: declare i1 @C()
// CHECK:STDOUT:
// CHECK:STDOUT: define void @While() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: br label %0
// CHECK:STDOUT:
// CHECK:STDOUT: 0: ; preds = %entry, %8, %4
// CHECK:STDOUT: %A = call i1 @A()
// CHECK:STDOUT: %temp = alloca i1, align 1
// CHECK:STDOUT: store i1 %A, ptr %temp, align 1
// CHECK:STDOUT: %1 = load i1, ptr %temp, align 1
// CHECK:STDOUT: br i1 %1, label %2, label %9
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %0
// CHECK:STDOUT: %B = call i1 @B()
// CHECK:STDOUT: %temp1 = alloca i1, align 1
// CHECK:STDOUT: store i1 %B, ptr %temp1, align 1
// CHECK:STDOUT: %3 = load i1, ptr %temp1, align 1
// CHECK:STDOUT: br i1 %3, label %4, label %5
// CHECK:STDOUT:
// CHECK:STDOUT: 4: ; preds = %2
// CHECK:STDOUT: br label %0
// CHECK:STDOUT:
// CHECK:STDOUT: 5: ; preds = %2
// CHECK:STDOUT: %C = call i1 @C()
// CHECK:STDOUT: %temp2 = alloca i1, align 1
// CHECK:STDOUT: store i1 %C, ptr %temp2, align 1
// CHECK:STDOUT: %6 = load i1, ptr %temp2, align 1
// CHECK:STDOUT: br i1 %6, label %7, label %8
// CHECK:STDOUT:
// CHECK:STDOUT: 7: ; preds = %5
// CHECK:STDOUT: br label %9
// CHECK:STDOUT:
// CHECK:STDOUT: 8: ; preds = %5
// CHECK:STDOUT: br label %0
// CHECK:STDOUT:
// CHECK:STDOUT: 9: ; preds = %7, %0
// CHECK:STDOUT: ret void
// CHECK:STDOUT:
// CHECK:STDOUT: ; uselistorder directives
// CHECK:STDOUT: uselistorder label %0, { 1, 2, 0 }
// CHECK:STDOUT: }
+77
View File
@@ -0,0 +1,77 @@
// 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
fn Cond() -> bool;
fn F();
fn G();
// TODO: It would be better to preserve the loop preheader blocks from SemIR
// into LLVM IR. If we don't, LLVM will recreate them for us as part of loop
// canonicalization.
fn While() {
while (Cond()) {
F();
}
if (Cond()) {
while (Cond()) {
G();
}
}
}
// CHECK:STDOUT: ; ModuleID = 'preheader.carbon'
// CHECK:STDOUT: source_filename = "preheader.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: declare i1 @Cond()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @F()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @G()
// CHECK:STDOUT:
// CHECK:STDOUT: define void @While() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: br label %0
// CHECK:STDOUT:
// CHECK:STDOUT: 0: ; preds = %entry, %2
// CHECK:STDOUT: %Cond = call i1 @Cond()
// CHECK:STDOUT: %temp = alloca i1, align 1
// CHECK:STDOUT: store i1 %Cond, ptr %temp, align 1
// CHECK:STDOUT: %1 = load i1, ptr %temp, align 1
// CHECK:STDOUT: br i1 %1, label %2, label %3
// CHECK:STDOUT:
// CHECK:STDOUT: 2: ; preds = %0
// CHECK:STDOUT: call void @F()
// CHECK:STDOUT: br label %0
// CHECK:STDOUT:
// CHECK:STDOUT: 3: ; preds = %0
// CHECK:STDOUT: %Cond1 = call i1 @Cond()
// CHECK:STDOUT: %temp2 = alloca i1, align 1
// CHECK:STDOUT: store i1 %Cond1, ptr %temp2, align 1
// CHECK:STDOUT: %4 = load i1, ptr %temp2, align 1
// CHECK:STDOUT: br i1 %4, label %5, label %9
// CHECK:STDOUT:
// CHECK:STDOUT: 5: ; preds = %7, %3
// CHECK:STDOUT: %Cond3 = call i1 @Cond()
// CHECK:STDOUT: %temp4 = alloca i1, align 1
// CHECK:STDOUT: store i1 %Cond3, ptr %temp4, align 1
// CHECK:STDOUT: %6 = load i1, ptr %temp4, align 1
// CHECK:STDOUT: br i1 %6, label %7, label %8
// CHECK:STDOUT:
// CHECK:STDOUT: 7: ; preds = %5
// CHECK:STDOUT: call void @G()
// CHECK:STDOUT: br label %5
// CHECK:STDOUT:
// CHECK:STDOUT: 8: ; preds = %5
// CHECK:STDOUT: br label %9
// CHECK:STDOUT:
// CHECK:STDOUT: 9: ; preds = %8, %3
// CHECK:STDOUT: ret void
// CHECK:STDOUT:
// CHECK:STDOUT: ; uselistorder directives
// CHECK:STDOUT: uselistorder label %0, { 1, 0 }
// CHECK:STDOUT: }
+51
View File
@@ -0,0 +1,51 @@
// 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
fn Cond() -> bool;
fn F();
fn G();
fn H();
fn While() {
F();
while (Cond()) {
G();
return;
}
H();
}
// CHECK:STDOUT: ; ModuleID = 'unreachable_end.carbon'
// CHECK:STDOUT: source_filename = "unreachable_end.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: declare i1 @Cond()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @F()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @G()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @H()
// CHECK:STDOUT:
// CHECK:STDOUT: define void @While() {
// CHECK:STDOUT: call void @F()
// CHECK:STDOUT: br label %1
// CHECK:STDOUT:
// CHECK:STDOUT: 1: ; preds = %0
// CHECK:STDOUT: %Cond = call i1 @Cond()
// CHECK:STDOUT: %temp = alloca i1, align 1
// CHECK:STDOUT: store i1 %Cond, ptr %temp, align 1
// CHECK:STDOUT: %2 = load i1, ptr %temp, align 1
// CHECK:STDOUT: br i1 %2, label %3, label %4
// CHECK:STDOUT:
// CHECK:STDOUT: 3: ; preds = %1
// CHECK:STDOUT: call void @G()
// CHECK:STDOUT: ret void
// CHECK:STDOUT:
// CHECK:STDOUT: 4: ; preds = %1
// CHECK:STDOUT: call void @H()
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
+50
View File
@@ -0,0 +1,50 @@
// 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
fn Cond() -> bool;
fn F();
fn G();
fn H();
fn While() {
F();
while (Cond()) {
G();
}
H();
}
// CHECK:STDOUT: ; ModuleID = 'while.carbon'
// CHECK:STDOUT: source_filename = "while.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: declare i1 @Cond()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @F()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @G()
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @H()
// CHECK:STDOUT:
// CHECK:STDOUT: define void @While() {
// CHECK:STDOUT: call void @F()
// CHECK:STDOUT: br label %1
// CHECK:STDOUT:
// CHECK:STDOUT: 1: ; preds = %3, %0
// CHECK:STDOUT: %Cond = call i1 @Cond()
// CHECK:STDOUT: %temp = alloca i1, align 1
// CHECK:STDOUT: store i1 %Cond, ptr %temp, align 1
// CHECK:STDOUT: %2 = load i1, ptr %temp, align 1
// CHECK:STDOUT: br i1 %2, label %3, label %4
// CHECK:STDOUT:
// CHECK:STDOUT: 3: ; preds = %1
// CHECK:STDOUT: call void @G()
// CHECK:STDOUT: br label %1
// CHECK:STDOUT:
// CHECK:STDOUT: 4: ; preds = %1
// CHECK:STDOUT: call void @H()
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
+17
View File
@@ -315,6 +315,23 @@ class NodeNamer {
break;
}
case Parse::NodeKind::WhileConditionStart:
name = "while.cond";
break;
case Parse::NodeKind::WhileCondition:
switch (node.kind()) {
case NodeKind::BranchIf:
name = "while.body";
break;
case NodeKind::Branch:
name = "while.done";
break;
default:
break;
}
break;
default:
break;
}