``blockaddress(@function, %block)``
The '``blockaddress``' constant computes the address of the specified
-basic block in the specified function, and always has an ``i8*`` type.
+basic block in the specified function.
+
+It always has an ``i8 addrspace(P)*`` type, where ``P`` is the address space
+of the function containing ``%block`` (usually ``addrspace(0)``).
+
Taking the address of the entry block is illegal.
This value only has defined behavior when used as an operand to the
PerFunctionState &PFS);
// Constant Parsing.
- bool parseValID(ValID &ID, PerFunctionState *PFS = nullptr);
+ bool parseValID(ValID &ID, PerFunctionState *PFS,
+ Type *ExpectedTy = nullptr);
bool parseGlobalValue(Type *Ty, Constant *&C);
bool parseGlobalTypeAndValue(Constant *&V);
bool parseGlobalValueVector(SmallVectorImpl<Constant *> &Elts,
} else {
// The bitcast dest type is not present, it is implied by the dest type.
ValID ID;
- if (parseValID(ID))
+ if (parseValID(ID, /*PFS=*/nullptr))
return true;
if (ID.Kind != ValID::t_Constant)
return error(AliaseeLoc, "invalid aliasee");
/// sanity. PFS is used to convert function-local operands of metadata (since
/// metadata operands are not just parsed here but also converted to values).
/// PFS can be null when we are not parsing metadata values inside a function.
-bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS) {
+bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
ID.Loc = Lex.getLoc();
switch (Lex.getKind()) {
default:
ValID Fn, Label;
if (parseToken(lltok::lparen, "expected '(' in block address expression") ||
- parseValID(Fn) ||
+ parseValID(Fn, PFS) ||
parseToken(lltok::comma,
"expected comma in block address expression") ||
- parseValID(Label) ||
+ parseValID(Label, PFS) ||
parseToken(lltok::rparen, "expected ')' in block address expression"))
return true;
std::map<ValID, GlobalValue *>()))
.first->second.insert(std::make_pair(std::move(Label), nullptr))
.first->second;
- if (!FwdRef)
- FwdRef = new GlobalVariable(*M, Type::getInt8Ty(Context), false,
- GlobalValue::InternalLinkage, nullptr, "");
+ if (!FwdRef) {
+ unsigned FwdDeclAS;
+ if (ExpectedTy) {
+ // If we know the type that the blockaddress is being assigned to,
+ // we can use the address space of that type.
+ if (!ExpectedTy->isPointerTy())
+ return error(ID.Loc,
+ "type of blockaddress must be a pointer and not '" +
+ getTypeString(ExpectedTy) + "'");
+ FwdDeclAS = ExpectedTy->getPointerAddressSpace();
+ } else if (PFS) {
+ // Otherwise, we default the address space of the current function.
+ FwdDeclAS = PFS->getFunction().getAddressSpace();
+ } else {
+ llvm_unreachable("Unknown address space for blockaddress");
+ }
+ FwdRef = new GlobalVariable(
+ *M, Type::getInt8Ty(Context), false, GlobalValue::InternalLinkage,
+ nullptr, "", nullptr, GlobalValue::NotThreadLocal, FwdDeclAS);
+ }
+
ID.ConstantVal = FwdRef;
ID.Kind = ValID::t_Constant;
return false;
ValID Fn;
- if (parseValID(Fn))
+ if (parseValID(Fn, PFS))
return true;
if (Fn.Kind != ValID::t_GlobalID && Fn.Kind != ValID::t_GlobalName)
C = nullptr;
ValID ID;
Value *V = nullptr;
- bool Parsed = parseValID(ID) ||
+ bool Parsed = parseValID(ID, /*PFS=*/nullptr, Ty) ||
convertValIDToValue(Ty, ID, V, nullptr, /*IsCall=*/false);
if (V && !(C = dyn_cast<Constant>(V)))
return error(ID.Loc, "global values must be constants");
return false;
case ValID::t_Constant:
if (ID.ConstantVal->getType() != Ty)
- return error(ID.Loc, "constant expression type mismatch");
+ return error(ID.Loc, "constant expression type mismatch: got type '" +
+ getTypeString(ID.ConstantVal->getType()) +
+ "' but expected '" + getTypeString(Ty) + "'");
V = ID.ConstantVal;
return false;
case ValID::t_ConstantStruct:
bool LLParser::parseValue(Type *Ty, Value *&V, PerFunctionState *PFS) {
V = nullptr;
ValID ID;
- return parseValID(ID, PFS) ||
+ return parseValID(ID, PFS, Ty) ||
convertValIDToValue(Ty, ID, V, PFS, /*IsCall=*/false);
}
if (!BB)
return P.error(BBID.Loc, "referenced value is not a basic block");
- GV->replaceAllUsesWith(BlockAddress::get(&F, BB));
+ Value *ResolvedVal = BlockAddress::get(&F, BB);
+ ResolvedVal = P.checkValidVariableType(BBID.Loc, BBID.StrVal, GV->getType(),
+ ResolvedVal, false);
+ if (!ResolvedVal)
+ return true;
+ GV->replaceAllUsesWith(ResolvedVal);
GV->eraseFromParent();
}
if (parseOptionalCallingConv(CC) || parseOptionalReturnAttrs(RetAttrs) ||
parseOptionalProgramAddrSpace(InvokeAddrSpace) ||
parseType(RetType, RetTypeLoc, true /*void allowed*/) ||
- parseValID(CalleeID) || parseParameterList(ArgList, PFS) ||
+ parseValID(CalleeID, &PFS) || parseParameterList(ArgList, PFS) ||
parseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false,
NoBuiltinLoc) ||
parseOptionalOperandBundles(BundleList, PFS) ||
BasicBlock *DefaultDest;
if (parseOptionalCallingConv(CC) || parseOptionalReturnAttrs(RetAttrs) ||
parseType(RetType, RetTypeLoc, true /*void allowed*/) ||
- parseValID(CalleeID) || parseParameterList(ArgList, PFS) ||
+ parseValID(CalleeID, &PFS) || parseParameterList(ArgList, PFS) ||
parseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false,
NoBuiltinLoc) ||
parseOptionalOperandBundles(BundleList, PFS) ||
if (parseOptionalCallingConv(CC) || parseOptionalReturnAttrs(RetAttrs) ||
parseOptionalProgramAddrSpace(CallAddrSpace) ||
parseType(RetType, RetTypeLoc, true /*void allowed*/) ||
- parseValID(CalleeID) ||
+ parseValID(CalleeID, &PFS) ||
parseParameterList(ArgList, PFS, TCK == CallInst::TCK_MustTail,
PFS.getFunction().isVarArg()) ||
parseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false, BuiltinLoc) ||
ValID Fn, Label;
SmallVector<unsigned, 16> Indexes;
- if (parseValID(Fn) ||
+ if (parseValID(Fn, /*PFS=*/nullptr) ||
parseToken(lltok::comma, "expected comma in uselistorder_bb directive") ||
- parseValID(Label) ||
+ parseValID(Label, /*PFS=*/nullptr) ||
parseToken(lltok::comma, "expected comma in uselistorder_bb directive") ||
parseUseListOrderIndexes(Indexes))
return true;
}
BlockAddress::BlockAddress(Function *F, BasicBlock *BB)
-: Constant(Type::getInt8PtrTy(F->getContext()), Value::BlockAddressVal,
- &Op<0>(), 2) {
+ : Constant(Type::getInt8PtrTy(F->getContext(), F->getAddressSpace()),
+ Value::BlockAddressVal, &Op<0>(), 2) {
setOperand(0, F);
setOperand(1, BB);
BB->AdjustBlockAddressRefCount(1);
--- /dev/null
+; RUN: rm -rf %t && split-file %s %t
+; RUN: llvm-as %t/global-use-good.ll -o - | llvm-dis -o /dev/null
+; RUN: not llvm-as %t/global-use-bad.ll -o /dev/null 2>&1 | FileCheck %t/global-use-bad.ll
+; RUN: llvm-as %t/global-fwddecl-good.ll -o - | llvm-dis -o /dev/null
+; RUN: not llvm-as %t/global-fwddecl-bad.ll -o /dev/null 2>&1 | FileCheck %t/global-fwddecl-bad.ll
+; RUN: llvm-as %t/return-fwddecl-good.ll -o - | llvm-dis -o /dev/null
+; RUN: not llvm-as %t/return-fwddecl-bad.ll -o /dev/null 2>&1 | FileCheck %t/return-fwddecl-bad.ll
+; RUN: llvm-as %t/return-self-good.ll -o - | llvm-dis -o /dev/null
+; RUN: not llvm-as %t/return-self-bad.ll -o /dev/null 2>&1 | FileCheck %t/return-self-bad.ll
+; RUN: not llvm-as %t/return-self-bad-2.ll -o /dev/null 2>&1 | FileCheck %t/return-self-bad-2.ll
+; RUN: not llvm-as %t/return-unknown-fn-bad.ll -o /dev/null 2>&1 | FileCheck %t/return-unknown-fn-bad.ll
+; RUN: llvm-as %t/call-fwddecl-good.ll -o - | llvm-dis -o /dev/null
+; RUN: not llvm-as %t/call-fwddecl-bad.ll -o /dev/null 2>&1 | FileCheck %t/call-fwddecl-bad.ll
+; RUN: llvm-as %t/phi-good.ll -o - | llvm-dis -o /dev/null
+; RUN: not llvm-as %t/phi-bad.ll -o /dev/null 2>&1 | FileCheck %t/phi-bad.ll
+; RUN: llvm-as %t/fwddecl-phi-good.ll -o - | llvm-dis -o /dev/null
+; RUN: not llvm-as %t/fwddecl-phi-bad.ll -o /dev/null 2>&1 | FileCheck %t/fwddecl-phi-bad.ll
+; RUN: not llvm-as %t/bad-type-not-ptr.ll -o /dev/null 2>&1 | FileCheck %t/bad-type-not-ptr.ll
+; RUN: not llvm-as %t/bad-type-not-i8-ptr.ll -o /dev/null 2>&1 | FileCheck %t/bad-type-not-i8-ptr.ll
+
+
+;--- global-use-good.ll
+target datalayout = "P2"
+define void @fn_in_prog_as_implicit() {
+ unreachable
+bb:
+ ret void
+}
+define void @fn_in_prog_as_explicit() addrspace(2) {
+ unreachable
+bb:
+ ret void
+}
+define void @fn_in_other_as() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+@global1 = constant i8 addrspace(2)* blockaddress(@fn_in_prog_as_implicit, %bb)
+@global2 = constant i8 addrspace(2)* blockaddress(@fn_in_prog_as_explicit, %bb)
+@global3 = constant i8 addrspace(1)* blockaddress(@fn_in_other_as, %bb)
+
+;--- global-use-bad.ll
+define void @fn() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+@global1 = constant i8 addrspace(2)* blockaddress(@fn, %bb)
+; CHECK: [[#@LINE-1]]:38: error: constant expression type mismatch: got type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
+
+; Check that a global blockaddress of a forward-declared function
+; uses the type of the global variable address space for the forward declaration
+;--- global-fwddecl-good.ll
+@global = constant i8 addrspace(2)* blockaddress(@fwddecl_in_prog_as, %bb)
+define void @fwddecl_in_prog_as() addrspace(2) {
+ unreachable
+bb:
+ ret void
+}
+
+;--- global-fwddecl-bad.ll
+; This forward declaration does not match the actual function type so we should get an error:
+@global = constant i8 addrspace(2)* blockaddress(@fwddecl_in_unexpected_as, %bb)
+; CHECK: [[#@LINE-1]]:77: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
+define void @fwddecl_in_unexpected_as() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+
+
+; When returning blockaddresses of forward-declared functions we
+; can also use the type of the variable.
+;--- return-fwddecl-good.ll
+define i8 addrspace(2)* @take_as2() {
+ ret i8 addrspace(2)* blockaddress(@fwddecl_as2, %bb)
+}
+define i8 addrspace(1)* @take_as1() {
+ ret i8 addrspace(1)* blockaddress(@fwddecl_as1, %bb)
+}
+define void @fwddecl_as1() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+define void @fwddecl_as2() addrspace(2) {
+ unreachable
+bb:
+ ret void
+}
+
+;--- return-fwddecl-bad.ll
+define i8 addrspace(2)* @take_bad() {
+ ret i8 addrspace(2)* blockaddress(@fwddecl_as1, %bb)
+ ; CHECK: [[#@LINE-1]]:51: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
+}
+define void @fwddecl_as1() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+
+;--- return-self-good.ll
+target datalayout = "P2"
+define i8 addrspace(0)* @take_self_as0() addrspace(0) {
+L1:
+ br label %L2
+L2:
+ ret i8 addrspace(0)* blockaddress(@take_self_as0, %L3)
+L3:
+ unreachable
+}
+define i8 addrspace(2)* @take_self_prog_as() {
+L1:
+ br label %L2
+L2:
+ ret i8 addrspace(2)* blockaddress(@take_self_prog_as, %L3)
+L3:
+ unreachable
+}
+define i8 addrspace(1)* @take_self_as1() addrspace(1) {
+L1:
+ br label %L2
+L2:
+ ret i8 addrspace(1)* blockaddress(@take_self_as1, %L3)
+L3:
+ unreachable
+}
+define i8 addrspace(2)* @take_self_as2() addrspace(2) {
+L1:
+ br label %L2
+L2:
+ ret i8 addrspace(2)* blockaddress(@take_self_as2, %L3)
+L3:
+ unreachable
+}
+
+;--- return-self-bad.ll
+target datalayout = "P2"
+define i8 addrspace(2)* @take_self_bad() addrspace(1) {
+L1:
+ br label %L2
+L2:
+ ret i8 addrspace(2)* blockaddress(@take_self_bad, %L3)
+ ; CHECK: [[#@LINE-1]]:24: error: constant expression type mismatch: got type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
+L3:
+ unreachable
+}
+;--- return-self-bad-2.ll
+target datalayout = "P2"
+define i8* @take_self_bad_prog_as() {
+L1:
+ br label %L2
+L2:
+ ret i8* blockaddress(@take_self_bad_prog_as, %L3)
+ ; CHECK: [[#@LINE-1]]:11: error: constant expression type mismatch: got type 'i8 addrspace(2)*' but expected 'i8*'
+L3:
+ unreachable
+}
+
+;--- return-unknown-fn-bad.ll
+target datalayout = "P2"
+define i8 addrspace(1)* @return_unknown_fn() addrspace(1) {
+ ret i8 addrspace(1)* blockaddress(@undefined, %bb)
+ ; CHECK: [[#@LINE-1]]:37: error: expected function name in blockaddress
+}
+
+
+;--- call-fwddecl-good.ll
+target datalayout = "P2"
+define void @call_from_fn_in_as2() addrspace(2) {
+ call addrspace(2) void bitcast (i8 addrspace(2)* blockaddress(@fwddecl_as2, %bb) to void () addrspace(2)*)()
+ ret void
+}
+define void @call_from_fn_in_as1() addrspace(1) {
+ call addrspace(1) void bitcast (i8 addrspace(1)* blockaddress(@fwddecl_as1, %bb) to void () addrspace(1)*)()
+ ret void
+}
+define void @fwddecl_as2() addrspace(2) {
+ unreachable
+bb:
+ ret void
+}
+define void @fwddecl_as1() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+
+;--- call-fwddecl-bad.ll
+target datalayout = "P2"
+define void @call_from_fn_in_as2_explicit() addrspace(2) {
+ call addrspace(2) void bitcast (i8 addrspace(2)* blockaddress(@fwddecl_as1, %bb) to void () addrspace(2)*)()
+ ; CHECK: [[#@LINE-1]]:79: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
+ ret void
+}
+define void @fwddecl_as1() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+
+;--- phi-good.ll
+target datalayout = "P2"
+define i8 addrspace(1)* @f1() addrspace(1) {
+L1:
+ br label %L3
+L2:
+ br label %L3
+L3:
+ %p = phi i8 addrspace(1)* [ blockaddress(@f1, %L4), %L2 ], [ null, %L1 ]
+ ret i8 addrspace(1)* %p
+L4:
+ unreachable
+}
+define i8 addrspace(2)* @f2() {
+L1:
+ br label %L3
+L2:
+ br label %L3
+L3:
+ %p = phi i8 addrspace(2)* [ blockaddress(@f2, %L4), %L2 ], [ null, %L1 ]
+ ret i8 addrspace(2)* %p
+L4:
+ unreachable
+}
+
+;--- phi-bad.ll
+target datalayout = "P2"
+define i8* @f() {
+L1:
+ br label %L3
+L2:
+ br label %L3
+L3:
+ %p = phi i8* [ blockaddress(@f, %L4), %L2 ], [ null, %L1 ]
+ ; CHECK: [[#@LINE-1]]:18: error: constant expression type mismatch: got type 'i8 addrspace(2)*' but expected 'i8*'
+ ret i8* %p
+}
+
+; A blockaddress function forward-declaration used in a phi node should
+; create the forward declaration in the same address space as the current function
+;--- fwddecl-phi-good.ll
+define i8 addrspace(1)* @f() addrspace(1) {
+L1:
+ br label %L3
+L2:
+ br label %L3
+L3:
+ %p = phi i8 addrspace(1)* [ blockaddress(@fwddecl_as1, %bb), %L2 ], [ null, %L1 ]
+ ret i8 addrspace(1)* %p
+L4:
+ unreachable
+}
+define void @fwddecl_as1() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+
+;--- fwddecl-phi-bad.ll
+define i8 addrspace(2)* @f() addrspace(2) {
+L1:
+ br label %L3
+L2:
+ br label %L3
+L3:
+ %p = phi i8 addrspace(2)* [ blockaddress(@fwddecl_as1, %bb), %L2 ], [ null, %L1 ]
+ ; CHECK: [[#@LINE-1]]:58: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
+ ret i8 addrspace(2)* %p
+L4:
+ unreachable
+}
+define void @fwddecl_as1() addrspace(1) {
+ unreachable
+bb:
+ ret void
+}
+
+;--- bad-type-not-ptr.ll
+@global = constant i8 blockaddress(@unknown_fn, %bb)
+; CHECK: [[#@LINE-1]]:23: error: type of blockaddress must be a pointer and not 'i8'
+;--- bad-type-not-i8-ptr.ll
+@global = constant i32* blockaddress(@unknown_fn, %bb)
+; CHECK: [[#@LINE-1]]:25: error: constant expression type mismatch: got type 'i8*' but expected 'i32*'
--- /dev/null
+; RUN: llc -mcpu=atmega328 < %s -march=avr | FileCheck %s
+
+; This test verifies that the pointer to a basic block
+; should always be a pointer in address space 1.
+;
+; If this were not the case, then programs targeting
+; AVR that attempted to read their own machine code
+; via a pointer to a label would actually read from RAM
+; using a pointer relative to the the start of program flash.
+;
+; This would cause a load of uninitialized memory, not even
+; touching the program's machine code as otherwise desired.
+
+target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
+
+; CHECK-LABEL: load_with_no_forward_reference
+define i8 @load_with_no_forward_reference(i8 %a, i8 %b) {
+second:
+ ; CHECK: ldi r30, .Ltmp0+2
+ ; CHECK-NEXT: ldi r31, .Ltmp0+4
+ ; CHECK: lpm r24, Z
+ %bar = load i8, i8 addrspace(1)* blockaddress(@function_with_no_forward_reference, %second)
+ ret i8 %bar
+}
+
+; CHECK-LABEL: load_from_local_label
+define i8 @load_from_local_label(i8 %a, i8 %b) {
+entry:
+ %result1 = add i8 %a, %b
+
+ br label %second
+
+; CHECK-LABEL: .Ltmp1:
+second:
+ ; CHECK: ldi r30, .Ltmp1+2
+ ; CHECK-NEXT: ldi r31, .Ltmp1+4
+ ; CHECK-NEXT: lpm r24, Z
+ %result2 = load i8, i8 addrspace(1)* blockaddress(@load_from_local_label, %second)
+ ret i8 %result2
+}
+
+; A function with no forward reference, right at the end
+; of the file.
+define i8 @function_with_no_forward_reference(i8 %a, i8 %b) {
+entry:
+ %result = add i8 %a, %b
+ br label %second
+second:
+ ret i8 0
+}
+
; RUN: llc -mattr=sram,eijmpcall < %s -march=avr -verify-machineinstrs | FileCheck %s
-@brind.k = private unnamed_addr constant [2 x i8*] [i8* blockaddress(@brind, %return), i8* blockaddress(@brind, %b)], align 1
+@brind.k = private unnamed_addr constant [2 x i8 addrspace(1)*] [i8 addrspace(1)* blockaddress(@brind, %return), i8 addrspace(1)* blockaddress(@brind, %b)], align 1
define i8 @brind(i8 %p) {
; CHECK-LABEL: brind:
; CHECK: ijmp
entry:
%idxprom = sext i8 %p to i16
- %arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @brind.k, i16 0, i16 %idxprom
- %s = load i8*, i8** %arrayidx
- indirectbr i8* %s, [label %return, label %b]
+ %arrayidx = getelementptr inbounds [2 x i8 addrspace(1)*], [2 x i8 addrspace(1)*]* @brind.k, i16 0, i16 %idxprom
+ %s = load i8 addrspace(1)*, i8 addrspace(1)** %arrayidx
+ indirectbr i8 addrspace(1)* %s, [label %return, label %b]
b:
br label %return
return: