Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//===-- CIRGenBuilder.cpp - CIRBuilder implementation ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "CIRGenBuilder.h"

namespace cir {

mlir::Value CIRGenBuilderTy::maybeBuildArrayDecay(mlir::Location loc,
mlir::Value arrayPtr,
mlir::Type eltTy) {
auto arrayPtrTy =
::mlir::dyn_cast<::mlir::cir::PointerType>(arrayPtr.getType());
assert(arrayPtrTy && "expected pointer type");
auto arrayTy =
::mlir::dyn_cast<::mlir::cir::ArrayType>(arrayPtrTy.getPointee());

if (arrayTy) {
mlir::cir::PointerType flatPtrTy =
mlir::cir::PointerType::get(getContext(), arrayTy.getEltType());
return create<mlir::cir::CastOp>(
loc, flatPtrTy, mlir::cir::CastKind::array_to_ptrdecay, arrayPtr);
}

assert(arrayPtrTy.getPointee() == eltTy &&
"flat pointee type must match original array element type");
return arrayPtr;
}

mlir::Value CIRGenBuilderTy::getArrayElement(mlir::Location arrayLocBegin,
mlir::Location arrayLocEnd,
mlir::Value arrayPtr,
mlir::Type eltTy, mlir::Value idx,
bool shouldDecay) {
mlir::Value basePtr = arrayPtr;
if (shouldDecay)
basePtr = maybeBuildArrayDecay(arrayLocBegin, arrayPtr, eltTy);
mlir::Type flatPtrTy = basePtr.getType();
return create<mlir::cir::PtrStrideOp>(arrayLocEnd, flatPtrTy, basePtr, idx);
}

mlir::cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc,
llvm::APSInt intVal) {
bool isSigned = intVal.isSigned();
auto width = intVal.getBitWidth();
mlir::cir::IntType t = isSigned ? getSIntNTy(width) : getUIntNTy(width);
return getConstInt(loc, t,
isSigned ? intVal.getSExtValue() : intVal.getZExtValue());
}

mlir::cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc,
llvm::APInt intVal) {
auto width = intVal.getBitWidth();
mlir::cir::IntType t = getUIntNTy(width);
return getConstInt(loc, t, intVal.getZExtValue());
}

mlir::cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc,
mlir::Type t, uint64_t C) {
auto intTy = mlir::dyn_cast<mlir::cir::IntType>(t);
assert(intTy && "expected mlir::cir::IntType");
return create<mlir::cir::ConstantOp>(loc, intTy,
mlir::cir::IntAttr::get(t, C));
}
}; // namespace cir
31 changes: 17 additions & 14 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,22 +508,12 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
mlir::cir::IntAttr::get(uInt64Ty, C));
}

mlir::cir::ConstantOp getConstInt(mlir::Location loc, llvm::APSInt intVal) {
bool isSigned = intVal.isSigned();
auto width = intVal.getBitWidth();
mlir::cir::IntType t = isSigned ? getSIntNTy(width) : getUIntNTy(width);
return getConstInt(
loc, t, isSigned ? intVal.getSExtValue() : intVal.getZExtValue());
}
mlir::cir::ConstantOp getConstInt(mlir::Location loc, llvm::APSInt intVal);

mlir::cir::ConstantOp getConstInt(mlir::Location loc, mlir::Type t,
uint64_t C) {
auto intTy = mlir::dyn_cast<mlir::cir::IntType>(t);
assert(intTy && "expected mlir::cir::IntType");
return create<mlir::cir::ConstantOp>(loc, intTy,
mlir::cir::IntAttr::get(t, C));
}
mlir::cir::ConstantOp getConstInt(mlir::Location loc, llvm::APInt intVal);

mlir::cir::ConstantOp getConstInt(mlir::Location loc, mlir::Type t,
uint64_t C);
/// Create constant nullptr for pointer-to-data-member type ty.
mlir::cir::ConstantOp getNullDataMemberPtr(mlir::cir::DataMemberType ty,
mlir::Location loc) {
Expand Down Expand Up @@ -959,6 +949,19 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
return create<mlir::cir::GetRuntimeMemberOp>(loc, resultTy, objectPtr,
memberPtr);
}

/// Create a cir.ptr_stride operation to get access to an array element.
/// idx is the index of the element to access, shouldDecay is true if the
/// result should decay to a pointer to the element type.
mlir::Value getArrayElement(mlir::Location arrayLocBegin,
mlir::Location arrayLocEnd, mlir::Value arrayPtr,
mlir::Type eltTy, mlir::Value idx,
bool shouldDecay);

/// Returns a decayed pointer to the first element of the array
/// pointed to by arrayPtr.
mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr,
mlir::Type eltTy);
};

} // namespace cir
Expand Down
51 changes: 7 additions & 44 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// This contains code to emit Expr nodes as CIR code.
//
//===----------------------------------------------------------------------===//
#include "CIRGenBuilder.h"
#include "CIRGenCXXABI.h"
#include "CIRGenCall.h"
#include "CIRGenCstEmitter.h"
Expand Down Expand Up @@ -1508,28 +1507,6 @@ void CIRGenFunction::buildIgnoredExpr(const Expr *E) {
buildLValue(E);
}

static mlir::Value maybeBuildArrayDecay(mlir::OpBuilder &builder,
mlir::Location loc,
mlir::Value arrayPtr,
mlir::Type eltTy) {
auto arrayPtrTy =
::mlir::dyn_cast<::mlir::cir::PointerType>(arrayPtr.getType());
assert(arrayPtrTy && "expected pointer type");
auto arrayTy =
::mlir::dyn_cast<::mlir::cir::ArrayType>(arrayPtrTy.getPointee());

if (arrayTy) {
mlir::cir::PointerType flatPtrTy =
mlir::cir::PointerType::get(builder.getContext(), arrayTy.getEltType());
return builder.create<mlir::cir::CastOp>(
loc, flatPtrTy, mlir::cir::CastKind::array_to_ptrdecay, arrayPtr);
}

assert(arrayPtrTy.getPointee() == eltTy &&
"flat pointee type must match original array element type");
return arrayPtr;
}

Address CIRGenFunction::buildArrayToPointerDecay(const Expr *E,
LValueBaseInfo *BaseInfo) {
assert(E->getType()->isArrayType() &&
Expand Down Expand Up @@ -1566,8 +1543,8 @@ Address CIRGenFunction::buildArrayToPointerDecay(const Expr *E,
*BaseInfo = LV.getBaseInfo();
assert(!MissingFeatures::tbaa() && "NYI");

mlir::Value ptr = maybeBuildArrayDecay(
CGM.getBuilder(), CGM.getLoc(E->getSourceRange()), Addr.getPointer(),
mlir::Value ptr = CGM.getBuilder().maybeBuildArrayDecay(
CGM.getLoc(E->getSourceRange()), Addr.getPointer(),
getTypes().convertTypeForMem(EltType));
return Address(ptr, Addr.getAlignment());
}
Expand Down Expand Up @@ -1648,20 +1625,6 @@ static CharUnits getArrayElementAlign(CharUnits arrayAlign, mlir::Value idx,
}
}

static mlir::Value buildArrayAccessOp(mlir::OpBuilder &builder,
mlir::Location arrayLocBegin,
mlir::Location arrayLocEnd,
mlir::Value arrayPtr, mlir::Type eltTy,
mlir::Value idx, bool shouldDecay) {
mlir::Value basePtr = arrayPtr;
if (shouldDecay)
basePtr = maybeBuildArrayDecay(builder, arrayLocBegin, arrayPtr, eltTy);
mlir::Type flatPtrTy = basePtr.getType();

return builder.create<mlir::cir::PtrStrideOp>(arrayLocEnd, flatPtrTy, basePtr,
idx);
}

static mlir::Value
buildArraySubscriptPtr(CIRGenFunction &CGF, mlir::Location beginLoc,
mlir::Location endLoc, mlir::Value ptr, mlir::Type eltTy,
Expand All @@ -1675,8 +1638,8 @@ buildArraySubscriptPtr(CIRGenFunction &CGF, mlir::Location beginLoc,
// that would enhance tracking this later in CIR?
if (inbounds)
assert(!MissingFeatures::emitCheckedInBoundsGEP() && "NYI");
return buildArrayAccessOp(CGM.getBuilder(), beginLoc, endLoc, ptr, eltTy, idx,
shouldDecay);
return CGM.getBuilder().getArrayElement(beginLoc, endLoc, ptr, eltTy, idx,
shouldDecay);
}

static QualType getFixedSizeElementType(const ASTContext &ctx,
Expand Down Expand Up @@ -1717,7 +1680,7 @@ static Address buildArraySubscriptPtr(
// assert(indices.size() == 1 && "cannot handle multiple indices yet");
// auto idx = indices.back();
// auto &CGM = CGF.getCIRGenModule();
// eltPtr = buildArrayAccessOp(CGM.getBuilder(), beginLoc, endLoc,
// eltPtr = CGM.getBuilder().getArrayElement(beginLoc, endLoc,
// addr.getPointer(), addr.getElementType(),
// idx);
assert(0 && "NYI");
Expand Down Expand Up @@ -1999,8 +1962,8 @@ LValue CIRGenFunction::buildCastLValue(const CastExpr *E) {
case CK_AddressSpaceConversion: {
LValue LV = buildLValue(E->getSubExpr());
QualType DestTy = getContext().getPointerType(E->getType());
auto SrcAS = builder.getAddrSpaceAttr(
E->getSubExpr()->getType().getAddressSpace());
auto SrcAS =
builder.getAddrSpaceAttr(E->getSubExpr()->getType().getAddressSpace());
auto DestAS = builder.getAddrSpaceAttr(E->getType().getAddressSpace());
mlir::Value V = getTargetHooks().performAddrSpaceCast(
*this, LV.getPointer(), SrcAS, DestAS, ConvertType(DestTy));
Expand Down
60 changes: 58 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,64 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
}
void VisitLambdaExpr(LambdaExpr *E);
void VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) {
llvm_unreachable("NYI");
ASTContext &Ctx = CGF.getContext();
CIRGenFunction::SourceLocRAIIObject locRAIIObject{
CGF, CGF.getLoc(E->getSourceRange())};
// Emit an array containing the elements. The array is externally
// destructed if the std::initializer_list object is.
LValue Array = CGF.buildLValue(E->getSubExpr());
assert(Array.isSimple() && "initializer_list array not a simple lvalue");
Address ArrayPtr = Array.getAddress();

const ConstantArrayType *ArrayType =
Ctx.getAsConstantArrayType(E->getSubExpr()->getType());
assert(ArrayType && "std::initializer_list constructed from non-array");

RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
RecordDecl::field_iterator Field = Record->field_begin();
assert(Field != Record->field_end() &&
Ctx.hasSameType(Field->getType()->getPointeeType(),
ArrayType->getElementType()) &&
"Expected std::initializer_list first field to be const E *");
// Start pointer.
auto loc = CGF.getLoc(E->getSourceRange());
AggValueSlot Dest = EnsureSlot(loc, E->getType());
LValue DestLV = CGF.makeAddrLValue(Dest.getAddress(), E->getType());
LValue Start =
CGF.buildLValueForFieldInitialization(DestLV, *Field, Field->getName());
mlir::Value ArrayStart = ArrayPtr.emitRawPointer();
CGF.buildStoreThroughLValue(RValue::get(ArrayStart), Start);
++Field;
assert(Field != Record->field_end() &&
"Expected std::initializer_list to have two fields");

auto Builder = CGF.getBuilder();

auto sizeOp = Builder.getConstInt(loc, ArrayType->getSize());

mlir::Value Size = sizeOp.getRes();
Builder.getUIntNTy(ArrayType->getSizeBitWidth());
LValue EndOrLength =
CGF.buildLValueForFieldInitialization(DestLV, *Field, Field->getName());
if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) {
// Length.
CGF.buildStoreThroughLValue(RValue::get(Size), EndOrLength);
} else {
// End pointer.
assert(Field->getType()->isPointerType() &&
Ctx.hasSameType(Field->getType()->getPointeeType(),
ArrayType->getElementType()) &&
"Expected std::initializer_list second field to be const E *");

auto ArrayEnd =
Builder.getArrayElement(loc, loc, ArrayPtr.getPointer(),
ArrayPtr.getElementType(), Size, false);
CGF.buildStoreThroughLValue(RValue::get(ArrayEnd), EndOrLength);
}
assert(++Field == Record->field_end() &&
"Expected std::initializer_list to only have two fields");
}

void VisitExprWithCleanups(ExprWithCleanups *E);
void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) {
llvm_unreachable("NYI");
Expand Down Expand Up @@ -1671,4 +1727,4 @@ LValue CIRGenFunction::buildAggExprToLValue(const Expr *E) {
AggValueSlot::IsNotAliased,
AggValueSlot::DoesNotOverlap));
return LV;
}
}
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
add_clang_library(clangCIR
CIRAsm.cpp
CIRGenAtomic.cpp
CIRGenBuilder.cpp
CIRGenBuiltin.cpp
CIRGenBuiltinAArch64.cpp
CIRGenBuiltinX86.cpp
Expand Down
82 changes: 82 additions & 0 deletions clang/test/CIR/CodeGen/initlist-ptr-ptr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM

namespace std {
template <class b> class initializer_list {
const b *array_start;
const b *array_end;
};
template <class b>
void f(initializer_list<b>) {;}
void test() {
f({"xy","uv"});
}
} // namespace std

// CIR: [[INITLIST_TYPE:!.*]] = !cir.struct<class "std::initializer_list<const char *>" {!cir.ptr<!cir.ptr<!cir.int<s, 8>>>, !cir.ptr<!cir.ptr<!cir.int<s, 8>>>}>
// CIR: cir.func linkonce_odr @_ZSt1fIPKcEvSt16initializer_listIT_E(%arg0: [[INITLIST_TYPE]]
// CIR: [[LOCAL:%.*]] = cir.alloca [[INITLIST_TYPE]], !cir.ptr<[[INITLIST_TYPE]]>,
// CIR: cir.store %arg0, [[LOCAL]] : [[INITLIST_TYPE]], !cir.ptr<[[INITLIST_TYPE]]>
// CIR: cir.return

// CIR: cir.global "private" constant internal dsolocal [[STR_XY:@.*]] = #cir.const_array<"xy\00" : !cir.array<!s8i x 3>> : !cir.array<!s8i x 3>
// CIR: cir.global "private" constant internal dsolocal [[STR_UV:@.*]] = #cir.const_array<"uv\00" : !cir.array<!s8i x 3>> : !cir.array<!s8i x 3>

// CIR: cir.func @_ZSt4testv()
// CIR: cir.scope {
// CIR: [[INITLIST_LOCAL:%.*]] = cir.alloca [[INITLIST_TYPE]], !cir.ptr<[[INITLIST_TYPE]]>,
// CIR: [[LOCAL_ELEM_ARRAY:%.*]] = cir.alloca !cir.array<!cir.ptr<!s8i> x 2>, !cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>,
// CIR: [[FIRST_ELEM_PTR:%.*]] = cir.cast(array_to_ptrdecay, [[LOCAL_ELEM_ARRAY]] : !cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>), !cir.ptr<!cir.ptr<!s8i>>
// CIR: [[XY_CHAR_ARRAY:%.*]] = cir.get_global [[STR_XY]] : !cir.ptr<!cir.array<!s8i x 3>>
// CIR: [[STR_XY_PTR:%.*]] = cir.cast(array_to_ptrdecay, [[XY_CHAR_ARRAY]] : !cir.ptr<!cir.array<!s8i x 3>>), !cir.ptr<!s8i>
// CIR: cir.store [[STR_XY_PTR]], [[FIRST_ELEM_PTR]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
// CIR: [[ONE:%.*]] = cir.const #cir.int<1>
// CIR: [[NEXT_ELEM_PTR:%.*]] = cir.ptr_stride([[FIRST_ELEM_PTR]] : !cir.ptr<!cir.ptr<!s8i>>, [[ONE]] : !s64i), !cir.ptr<!cir.ptr<!s8i>>
// CIR: [[UV_CHAR_ARRAY:%.*]] = cir.get_global [[STR_UV]] : !cir.ptr<!cir.array<!s8i x 3>>
// CIR: [[STR_UV_PTR:%.*]] = cir.cast(array_to_ptrdecay, [[UV_CHAR_ARRAY]] : !cir.ptr<!cir.array<!s8i x 3>>), !cir.ptr<!s8i>
// CIR: cir.store [[STR_UV_PTR]], [[NEXT_ELEM_PTR]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
// CIR: [[START_FLD_PTR:%.*]] = cir.get_member [[INITLIST_LOCAL]][0] {name = "array_start"} : !cir.ptr<[[INITLIST_TYPE]]> -> !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
// CIR: [[START_FLD_PTR_AS_PTR_2_CHAR_ARRAY:%.*]] = cir.cast(bitcast, [[START_FLD_PTR]] : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>), !cir.ptr<!cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>>
// CIR: cir.store [[LOCAL_ELEM_ARRAY]], [[START_FLD_PTR_AS_PTR_2_CHAR_ARRAY]] : !cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>, !cir.ptr<!cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>>
// CIR: [[ELEM_ARRAY_LEN:%.*]] = cir.const #cir.int<2>
// CIR: [[END_FLD_PTR:%.*]] = cir.get_member [[INITLIST_LOCAL]][1] {name = "array_end"} : !cir.ptr<[[INITLIST_TYPE]]> -> !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
// CIR: [[LOCAL_ELEM_ARRAY_END:%.*]] = cir.ptr_stride([[LOCAL_ELEM_ARRAY]] : !cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>, [[ELEM_ARRAY_LEN]] : !u64i), !cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>
// CIR: [[END_FLD_PTR_AS_PTR_2_CHAR_ARRAY:%.*]] = cir.cast(bitcast, [[END_FLD_PTR]] : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>), !cir.ptr<!cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>>
// CIR: cir.store [[LOCAL_ELEM_ARRAY_END]], [[END_FLD_PTR_AS_PTR_2_CHAR_ARRAY]] : !cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>, !cir.ptr<!cir.ptr<!cir.array<!cir.ptr<!s8i> x 2>>>
// CIR: [[ARG:%.*]] = cir.load [[INITLIST_LOCAL]] : !cir.ptr<[[INITLIST_TYPE]]>, [[INITLIST_TYPE]]
// CIR: cir.call @_ZSt1fIPKcEvSt16initializer_listIT_E([[ARG]]) : ([[INITLIST_TYPE]]) -> ()
// CIR: }
// CIR: cir.return
// CIR: }

// LLVM: %"class.std::initializer_list<const char *>" = type { ptr, ptr }

// LLVM: @.str = internal constant [3 x i8] c"xy\00"
// LLVM: @.str1 = internal constant [3 x i8] c"uv\00"

// LLVM: define linkonce_odr void @_ZSt1fIPKcEvSt16initializer_listIT_E(%"class.std::initializer_list<const char *>" [[ARG0:%.*]])
// LLVM: [[LOCAL_PTR:%.*]] = alloca %"class.std::initializer_list<const char *>", i64 1, align 8,
// LLVM: store %"class.std::initializer_list<const char *>" [[ARG0]], ptr [[LOCAL_PTR]], align 8,
// LLVM: ret void,
// LLVM: }

// LLVM: define dso_local void @_ZSt4testv()
// LLVM: br label %[[SCOPE_START:.*]],
// LLVM: [[SCOPE_START]]: ; preds = %0
// LLVM: [[INIT_STRUCT:%.*]] = alloca %"class.std::initializer_list<const char *>", i64 1, align 8,
// LLVM: [[ELEM_ARRAY_PTR:%.*]] = alloca [2 x ptr], i64 1, align 8,
// LLVM: [[PTR_FIRST_ELEM:%.*]] = getelementptr ptr, ptr [[ELEM_ARRAY_PTR]], i32 0,
// LLVM: store ptr @.str, ptr [[PTR_FIRST_ELEM]], align 8,
// LLVM: [[PTR_SECOND_ELEM:%.*]] = getelementptr ptr, ptr [[PTR_FIRST_ELEM]], i64 1,
// LLVM: store ptr @.str1, ptr [[PTR_SECOND_ELEM]], align 8,
// LLVM: [[INIT_START_FLD_PTR:%.*]] = getelementptr %"class.std::initializer_list<const char *>", ptr [[INIT_STRUCT]], i32 0, i32 0,
// LLVM: [[INIT_END_FLD_PTR:%.*]] = getelementptr %"class.std::initializer_list<const char *>", ptr [[INIT_STRUCT]], i32 0, i32 1,
// LLVM: [[ELEM_ARRAY_END:%.*]] = getelementptr [2 x ptr], ptr [[ELEM_ARRAY_PTR]], i64 2,
// LLVM: store ptr [[ELEM_ARRAY_END]], ptr [[INIT_END_FLD_PTR]], align 8,
// LLVM: [[ARG2PASS:%.*]] = load %"class.std::initializer_list<const char *>", ptr [[INIT_STRUCT]], align 8,
// LLVM: call void @_ZSt1fIPKcEvSt16initializer_listIT_E(%"class.std::initializer_list<const char *>" [[ARG2PASS]]),
// LLVM: br label %[[SCOPE_END:.*]],
// LLVM: [[SCOPE_END]]: ; preds = %[[SCOPE_START]]
// LLVM: ret void
Loading