Skip to content

Commit

Permalink
Reapply: IR: add optional type to 'byval' function parameters
Browse files Browse the repository at this point in the history
When we switch to opaque pointer types we will need some way to describe
how many bytes a 'byval' parameter should occupy on the stack. This adds
a (for now) optional extra type parameter.

If present, the type must match the pointee type of the argument.

The original commit did not remap byval types when linking modules, which broke
LTO. This version fixes that.

Note to front-end maintainers: if this causes test failures, it's probably
because the "byval" attribute is printed after attributes without any parameter
after this change.

llvm-svn: 362128
  • Loading branch information
TNorthover committed May 30, 2019
1 parent 7fecdf3 commit b714120
Show file tree
Hide file tree
Showing 45 changed files with 497 additions and 42 deletions.
5 changes: 4 additions & 1 deletion llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ Currently, only the following parameter attributes are defined:
opposed to memory, though some targets use it to distinguish between
two different kinds of registers). Use of this attribute is
target-specific.
``byval``
``byval`` or ``byval(<ty>)``
This indicates that the pointer parameter should really be passed by
value to the function. The attribute implies that a hidden copy of
the pointee is made between the caller and the callee, so the callee
Expand All @@ -1029,6 +1029,9 @@ Currently, only the following parameter attributes are defined:
``byval`` parameters). This is not a valid attribute for return
values.

The byval attribute also supports an optional type argument, which must be
the same as the pointee type of the argument.

The byval attribute also supports specifying an alignment with the
align attribute. It indicates the alignment of the stack slot to
form and the known alignment of the pointer specified to the call
Expand Down
5 changes: 5 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ Changes to the LLVM IR
type is now mandatory. Specify `i8* null` to migrate from the obsoleted
2-field form.

* The ``byval`` attribute can now take a type parameter:
``byval(<ty>)``. If present it must be identical to the argument's
pointee type. In the next release we intend to make this parameter
mandatory in preparation for opaque pointer types.

Changes to the ARM Backend
--------------------------

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class TargetLoweringBase {
bool IsSwiftSelf : 1;
bool IsSwiftError : 1;
uint16_t Alignment = 0;
Type *ByValType = nullptr;

ArgListEntry()
: IsSExt(false), IsZExt(false), IsInReg(false), IsSRet(false),
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Argument.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class Argument final : public Value {
/// If this is a byval or inalloca argument, return its alignment.
unsigned getParamAlignment() const;

/// If this is a byval argument, return its type.
Type *getParamByValType() const;

/// Return true if this argument has the nest attribute.
bool hasNestAttr() const;

Expand Down
20 changes: 20 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class Attribute {
static Attribute get(LLVMContext &Context, AttrKind Kind, uint64_t Val = 0);
static Attribute get(LLVMContext &Context, StringRef Kind,
StringRef Val = StringRef());
static Attribute get(LLVMContext &Context, AttrKind Kind, Type *Ty);

/// Return a uniquified Attribute object that has the specific
/// alignment set.
Expand All @@ -102,6 +103,7 @@ class Attribute {
static Attribute getWithAllocSizeArgs(LLVMContext &Context,
unsigned ElemSizeArg,
const Optional<unsigned> &NumElemsArg);
static Attribute getWithByValType(LLVMContext &Context, Type *Ty);

//===--------------------------------------------------------------------===//
// Attribute Accessors
Expand All @@ -117,6 +119,9 @@ class Attribute {
/// attribute.
bool isStringAttribute() const;

/// Return true if the attribute is a type attribute.
bool isTypeAttribute() const;

/// Return true if the attribute is present.
bool hasAttribute(AttrKind Val) const;

Expand All @@ -139,6 +144,10 @@ class Attribute {
/// attribute to be a string attribute.
StringRef getValueAsString() const;

/// Return the attribute's value as a Type. This requires the attribute to be
/// a type attribute.
Type *getValueAsType() const;

/// Returns the alignment field of an attribute as a byte alignment
/// value.
unsigned getAlignment() const;
Expand Down Expand Up @@ -279,6 +288,7 @@ class AttributeSet {
unsigned getStackAlignment() const;
uint64_t getDereferenceableBytes() const;
uint64_t getDereferenceableOrNullBytes() const;
Type *getByValType() const;
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
std::string getAsString(bool InAttrGrp = false) const;

Expand Down Expand Up @@ -598,6 +608,9 @@ class AttributeList {
/// Return the alignment for the specified function parameter.
unsigned getParamAlignment(unsigned ArgNo) const;

/// Return the byval type for the specified function parameter.
Type *getParamByValType(unsigned ArgNo) const;

/// Get the stack alignment.
unsigned getStackAlignment(unsigned Index) const;

Expand Down Expand Up @@ -697,6 +710,7 @@ class AttrBuilder {
uint64_t DerefBytes = 0;
uint64_t DerefOrNullBytes = 0;
uint64_t AllocSizeArgs = 0;
Type *ByValType = nullptr;

public:
AttrBuilder() = default;
Expand Down Expand Up @@ -772,6 +786,9 @@ class AttrBuilder {
/// dereferenceable_or_null attribute exists (zero is returned otherwise).
uint64_t getDereferenceableOrNullBytes() const { return DerefOrNullBytes; }

/// Retrieve the byval type.
Type *getByValType() const { return ByValType; }

/// Retrieve the allocsize args, if the allocsize attribute exists. If it
/// doesn't exist, pair(0, 0) is returned.
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
Expand All @@ -796,6 +813,9 @@ class AttrBuilder {
AttrBuilder &addAllocSizeAttr(unsigned ElemSizeArg,
const Optional<unsigned> &NumElemsArg);

/// This turns a byval type into the form used internally in Attribute.
AttrBuilder &addByValAttr(Type *Ty);

/// Add an allocsize attribute, using the representation returned by
/// Attribute.getIntValue().
AttrBuilder &addAllocSizeAttrFromRawRepr(uint64_t RawAllocSizeRepr);
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/CallSite.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@ class CallSiteBase {
CALLSITE_DELEGATE_GETTER(getParamAlignment(ArgNo));
}

/// Extract the byval type for a call or parameter (nullptr=unknown).
Type *getParamByValType(unsigned ArgNo) const {
CALLSITE_DELEGATE_GETTER(getParamByValType(ArgNo));
}

/// Extract the number of dereferenceable bytes for a call or parameter
/// (0=unknown).
uint64_t getDereferenceableBytes(unsigned i) const {
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,11 @@ class Function : public GlobalObject, public ilist_node<Function> {
return AttributeSets.getParamAlignment(ArgNo);
}

/// Extract the byval type for a parameter (nullptr=unknown).
Type *getParamByValType(unsigned ArgNo) const {
return AttributeSets.getParamByValType(ArgNo);
}

/// Extract the number of dereferenceable bytes for a call or
/// parameter (0=unknown).
/// @param i AttributeList index, referring to a return value or argument.
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/InstrTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,11 @@ class CallBase : public Instruction {
return Attrs.getParamAlignment(ArgNo);
}

/// Extract the byval type for a call or parameter (nullptr=unknown).
Type *getParamByValType(unsigned ArgNo) const {
return Attrs.getParamByValType(ArgNo);
}

/// Extract the number of dereferenceable bytes for a call or
/// parameter (0=unknown).
uint64_t getDereferenceableBytes(unsigned i) const {
Expand Down
24 changes: 23 additions & 1 deletion llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1601,7 +1601,13 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
B.addAlignmentAttr(Alignment);
continue;
}
case lltok::kw_byval: B.addAttribute(Attribute::ByVal); break;
case lltok::kw_byval: {
Type *Ty;
if (ParseByValWithOptionalType(Ty))
return true;
B.addByValAttr(Ty);
continue;
}
case lltok::kw_dereferenceable: {
uint64_t Bytes;
if (ParseOptionalDerefAttrBytes(lltok::kw_dereferenceable, Bytes))
Expand Down Expand Up @@ -2454,6 +2460,22 @@ bool LLParser::ParseParameterList(SmallVectorImpl<ParamInfo> &ArgList,
return false;
}

/// ParseByValWithOptionalType
/// ::= byval
/// ::= byval(<ty>)
bool LLParser::ParseByValWithOptionalType(Type *&Result) {
Result = nullptr;
if (!EatIfPresent(lltok::kw_byval))
return true;
if (!EatIfPresent(lltok::lparen))
return false;
if (ParseType(Result))
return true;
if (!EatIfPresent(lltok::rparen))
return Error(Lex.getLoc(), "expected ')'");
return false;
}

/// ParseOptionalOperandBundles
/// ::= /*empty*/
/// ::= '[' OperandBundle [, OperandBundle ]* ']'
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ namespace llvm {
bool ParseFnAttributeValuePairs(AttrBuilder &B,
std::vector<unsigned> &FwdRefAttrGrps,
bool inAttrGrp, LocTy &BuiltinLoc);
bool ParseByValWithOptionalType(Type *&Result);

// Module Summary Index Parsing.
bool SkipModuleSummaryEntry();
Expand Down
50 changes: 47 additions & 3 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,10 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer {
return getFnValueByID(ValNo, Ty);
}

/// Upgrades old-style typeless byval attributes by adding the corresponding
/// argument's pointee type.
void propagateByValTypes(CallBase *CB);

/// Converts alignment exponent (i.e. power of two (or zero)) to the
/// corresponding alignment to use. If alignment is too large, returns
/// a corresponding error code.
Expand Down Expand Up @@ -1492,6 +1496,12 @@ Error BitcodeReader::parseAttributeGroupBlock() {
if (Error Err = parseAttrKind(Record[++i], &Kind))
return Err;

// Upgrade old-style byval attribute to one with a type, even if it's
// nullptr. We will have to insert the real type when we associate
// this AttributeList with a function.
if (Kind == Attribute::ByVal)
B.addByValAttr(nullptr);

B.addAttribute(Kind);
} else if (Record[i] == 1) { // Integer attribute
Attribute::AttrKind Kind;
Expand All @@ -1507,9 +1517,7 @@ Error BitcodeReader::parseAttributeGroupBlock() {
B.addDereferenceableOrNullAttr(Record[++i]);
else if (Kind == Attribute::AllocSize)
B.addAllocSizeAttrFromRawRepr(Record[++i]);
} else { // String attribute
assert((Record[i] == 3 || Record[i] == 4) &&
"Invalid attribute group entry");
} else if (Record[i] == 3 || Record[i] == 4) { // String attribute
bool HasValue = (Record[i++] == 4);
SmallString<64> KindStr;
SmallString<64> ValStr;
Expand All @@ -1527,6 +1535,15 @@ Error BitcodeReader::parseAttributeGroupBlock() {
}

B.addAttribute(KindStr.str(), ValStr.str());
} else {
assert((Record[i] == 5 || Record[i] == 6) &&
"Invalid attribute group entry");
bool HasType = Record[i] == 6;
Attribute::AttrKind Kind;
if (Error Err = parseAttrKind(Record[++i], &Kind))
return Err;
if (Kind == Attribute::ByVal)
B.addByValAttr(HasType ? getTypeByID(Record[++i]) : nullptr);
}
}

Expand Down Expand Up @@ -3028,6 +3045,17 @@ Error BitcodeReader::parseFunctionRecord(ArrayRef<uint64_t> Record) {
Func->setLinkage(getDecodedLinkage(RawLinkage));
Func->setAttributes(getAttributes(Record[4]));

// Upgrade any old-style byval without a type by propagating the argument's
// pointee type. There should be no opaque pointers where the byval type is
// implicit.
for (auto &Arg : Func->args()) {
if (Arg.hasByValAttr() && !Arg.getParamByValType()) {
Arg.removeAttr(Attribute::ByVal);
Arg.addAttr(Attribute::getWithByValType(
Context, Arg.getType()->getPointerElementType()));
}
}

unsigned Alignment;
if (Error Err = parseAlignmentValue(Record[5], Alignment))
return Err;
Expand Down Expand Up @@ -3441,6 +3469,19 @@ Error BitcodeReader::typeCheckLoadStoreInst(Type *ValType, Type *PtrType) {
return Error::success();
}

void BitcodeReader::propagateByValTypes(CallBase *CB) {
for (unsigned i = 0; i < CB->getNumArgOperands(); ++i) {
if (CB->paramHasAttr(i, Attribute::ByVal) &&
!CB->getAttribute(i, Attribute::ByVal).getValueAsType()) {
CB->removeParamAttr(i, Attribute::ByVal);
CB->addParamAttr(
i, Attribute::getWithByValType(
Context,
CB->getArgOperand(i)->getType()->getPointerElementType()));
}
}
}

/// Lazily parse the specified function body block.
Error BitcodeReader::parseFunctionBody(Function *F) {
if (Stream.EnterSubBlock(bitc::FUNCTION_BLOCK_ID))
Expand Down Expand Up @@ -4256,6 +4297,8 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
cast<InvokeInst>(I)->setCallingConv(
static_cast<CallingConv::ID>(CallingConv::MaxID & CCInfo));
cast<InvokeInst>(I)->setAttributes(PAL);
propagateByValTypes(cast<CallBase>(I));

break;
}
case bitc::FUNC_CODE_INST_RESUME: { // RESUME: [opval]
Expand Down Expand Up @@ -4731,6 +4774,7 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
TCK = CallInst::TCK_NoTail;
cast<CallInst>(I)->setTailCallKind(TCK);
cast<CallInst>(I)->setAttributes(PAL);
propagateByValTypes(cast<CallBase>(I));
if (FMF.any()) {
if (!isa<FPMathOperator>(I))
return error("Fast-math-flags specified for call without "
Expand Down
15 changes: 11 additions & 4 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() {
Record.push_back(1);
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
Record.push_back(Attr.getValueAsInt());
} else {
} else if (Attr.isStringAttribute()) {
StringRef Kind = Attr.getKindAsString();
StringRef Val = Attr.getValueAsString();

Expand All @@ -758,6 +758,13 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() {
Record.append(Val.begin(), Val.end());
Record.push_back(0);
}
} else {
assert(Attr.isTypeAttribute());
Type *Ty = Attr.getValueAsType();
Record.push_back(Ty ? 6 : 5);
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
if (Ty)
Record.push_back(VE.getTypeID(Attr.getValueAsType()));
}
}

Expand Down Expand Up @@ -4126,15 +4133,15 @@ void ModuleBitcodeWriter::write() {
// Emit blockinfo, which defines the standard abbreviations etc.
writeBlockInfo();

// Emit information describing all of the types in the module.
writeTypeTable();

// Emit information about attribute groups.
writeAttributeGroupTable();

// Emit information about parameter attributes.
writeAttributeTable();

// Emit information describing all of the types in the module.
writeTypeTable();

writeComdats();

// Emit top-level description of module, including target triple, inline asm,
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Bitcode/Writer/ValueEnumerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,9 +949,11 @@ void ValueEnumerator::incorporateFunction(const Function &F) {
incorporateFunctionMetadata(F);

// Adding function arguments to the value table.
for (const auto &I : F.args())
for (const auto &I : F.args()) {
EnumerateValue(&I);

if (I.hasAttribute(Attribute::ByVal) && I.getParamByValType())
EnumerateType(I.getParamByValType());
}
FirstFuncConstantID = Values.size();

// Add all function-level constants to the value table.
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ void CallLowering::setArgFlags(CallLowering::ArgInfo &Arg, unsigned OpIdx,

if (Arg.Flags.isByVal() || Arg.Flags.isInAlloca()) {
Type *ElementTy = cast<PointerType>(Arg.Ty)->getElementType();
Arg.Flags.setByValSize(DL.getTypeAllocSize(ElementTy));

auto Ty = Attrs.getAttribute(OpIdx, Attribute::ByVal).getValueAsType();
Arg.Flags.setByValSize(DL.getTypeAllocSize(Ty ? Ty : ElementTy));

// For ByVal, alignment should be passed from FE. BE will guess if
// this info is not there but there are cases it cannot get right.
unsigned FrameAlign;
Expand Down
Loading

0 comments on commit b714120

Please sign in to comment.