diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3809,7 +3809,17 @@ ``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``. For targets that place code and data +in the same address space (Von-Neumann architectures) a block address will +have the same address space as data pointers, usually ``addrspace(0)``. +Block addresses on targets that have different data and code address spaces +(Harvard architectures) will be in the program memory address space specified +in the target's data layout unless the target can place functions in +multiple different address spaces. + Taking the address of the entry block is illegal. This value only has defined behavior when used as an operand to the diff --git a/llvm/lib/AsmParser/LLParser.h b/llvm/lib/AsmParser/LLParser.h --- a/llvm/lib/AsmParser/LLParser.h +++ b/llvm/lib/AsmParser/LLParser.h @@ -506,7 +506,8 @@ 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 &Elts, diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -980,7 +980,7 @@ } 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"); @@ -3241,7 +3241,7 @@ /// 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: @@ -3412,10 +3412,10 @@ 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; @@ -3450,9 +3450,32 @@ std::map())) .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() || + !ExpectedTy->getPointerElementType()->isIntegerTy(8)) + return error( + ID.Loc, "type of blockaddress must be pointer to i8 and not '" + + getTypeString(ExpectedTy) + "'"); + assert(ExpectedTy->isPointerTy()); + FwdDeclAS = ExpectedTy->getPointerAddressSpace(); + } else if (PFS) { + // Otherwise, we default the address space of the current function. + FwdDeclAS = PFS->getFunction().getAddressSpace(); + } else { + // If that is not known fall back to the programm adress space. + // TODO: can this ever happen? I wasn't able to think of a test case + // where this would be triggered. + FwdDeclAS = M->getDataLayout().getProgramAddressSpace(); + } + 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; @@ -3489,7 +3512,7 @@ ValID Fn; - if (parseValID(Fn)) + if (parseValID(Fn, PFS)) return true; if (Fn.Kind != ValID::t_GlobalID && Fn.Kind != ValID::t_GlobalName) @@ -3876,7 +3899,7 @@ 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(V))) return error(ID.Loc, "global values must be constants"); @@ -5553,7 +5576,9 @@ 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: @@ -5613,7 +5638,7 @@ 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); } @@ -5902,7 +5927,12 @@ 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(); } @@ -6437,7 +6467,7 @@ 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) || @@ -6745,7 +6775,7 @@ 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) || @@ -7172,7 +7202,7 @@ 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) || @@ -7840,9 +7870,9 @@ ValID Fn, Label; SmallVector 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; diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -1788,8 +1788,8 @@ } 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); diff --git a/llvm/test/Bitcode/blockaddress-addrspace.ll b/llvm/test/Bitcode/blockaddress-addrspace.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/blockaddress-addrspace.ll @@ -0,0 +1,286 @@ +; 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 pointer to i8 and not 'i8' +;--- bad-type-not-i8-ptr.ll +@global = constant i32* blockaddress(@unknown_fn, %bb) +; CHECK: [[#@LINE-1]]:25: error: type of blockaddress must be pointer to i8 and not 'i32*' diff --git a/llvm/test/CodeGen/AVR/block-address-is-in-progmem-space.ll b/llvm/test/CodeGen/AVR/block-address-is-in-progmem-space.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/block-address-is-in-progmem-space.ll @@ -0,0 +1,51 @@ +; 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 +} + diff --git a/llvm/test/CodeGen/AVR/brind.ll b/llvm/test/CodeGen/AVR/brind.ll --- a/llvm/test/CodeGen/AVR/brind.ll +++ b/llvm/test/CodeGen/AVR/brind.ll @@ -1,15 +1,15 @@ ; 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: