Commit 27fddcc3 by John Porto

Subzero. Enables moar complex relocation offsets.

This CL allows ConstantRelocatables in to Subzero have symbolic constants. A symbolic constant is an assembly label whose value is not known during lowering, but it is well defined during code emission. For example, the following code is now possible in Subzero: foo: push $foo.bar jmp target nop nop foo.bar: ... BUG= R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1651163002 .
parent 9aef39af
......@@ -142,8 +142,8 @@ ifdef FORCEASM
# resulting nexe. So we just disable those tests for now.
FORCEASM_XTEST_EXCLUDES = -e x8632,sandbox,test_sync_atomic
FORCEASM_LIT_PARAM = --param=FORCEASM
# x86 sandboxing lit tests are disabled because llvm-mc uses different
# relocations for pushing return addresses onto the stack.
# x86 sandboxing lit tests are disabled because filetype=asm does not
# handle bundle_lock pad-to-end correctly.
# TODO(jpp): fix this.
FORCEASM_LIT_TEST_EXCLUDES = --filter='^(?!.*/x86/sandboxing.ll).*'
else
......
......@@ -238,9 +238,6 @@ class Assembler {
Assembler &operator=(const Assembler &) = delete;
public:
using InternalRelocationList =
std::vector<std::pair<const IceString, const SizeT>>;
enum AssemblerKind {
Asm_ARM32,
Asm_MIPS32,
......@@ -328,23 +325,12 @@ public:
AssemblerKind getKind() const { return Kind; }
void addRelocationAtCurrentPosition(const IceString &RelocName) {
if (!getPreliminary()) {
InternalRelocs.emplace_back(RelocName, getBufferSize());
}
}
const InternalRelocationList &getInternalRelocations() const {
return InternalRelocs;
}
protected:
explicit Assembler(AssemblerKind Kind)
: Kind(Kind), Allocator(), Buffer(*this) {}
private:
const AssemblerKind Kind;
InternalRelocationList InternalRelocs;
ArenaAllocator<32 * 1024> Allocator;
/// FunctionName and IsInternal are transferred from the original Cfg object,
......
......@@ -171,6 +171,7 @@ public:
Label *getOrCreateCfgNodeLabel(SizeT Number);
Label *getOrCreateLocalLabel(SizeT Number);
void bindLocalLabel(SizeT Number);
void bindRelocOffset(RelocOffset *Offset);
bool fixupIsPCRel(FixupKind Kind) const override {
// Currently assuming this is the only PC-rel relocation type used.
......
......@@ -106,6 +106,13 @@ void AssemblerX86Base<TraitsType>::bindLocalLabel(SizeT Number) {
}
template <typename TraitsType>
void AssemblerX86Base<TraitsType>::bindRelocOffset(RelocOffset *Offset) {
if (!getPreliminary()) {
Offset->setOffset(Buffer.getPosition());
}
}
template <typename TraitsType>
void AssemblerX86Base<TraitsType>::call(GPRRegister reg) {
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
emitRexB(RexTypeIrrelevant, reg);
......
......@@ -309,6 +309,8 @@ enum RandomizationPassesEnum {
RPE_num
};
using RelocOffsetArray = llvm::SmallVector<class RelocOffset *, 4>;
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEDEFS_H
......@@ -256,15 +256,6 @@ void ELFObjectWriter::writeFunctionCode(const IceString &FuncName,
SymTab->createDefinedSym(FuncName, SymbolType, SymbolBinding, Section,
OffsetInSection, SymbolSize);
StrTab->add(FuncName);
for (const auto &InternalReloc : Asm->getInternalRelocations()) {
const IceString &RelocName = InternalReloc.first;
constexpr uint8_t RelocSymbolType = STT_NOTYPE;
constexpr uint8_t RelocSymbolBinding = STB_LOCAL;
const SizeT RelocOffsetInSection = OffsetInSection + InternalReloc.second;
SymTab->createDefinedSym(RelocName, RelocSymbolType, RelocSymbolBinding,
Section, RelocOffsetInSection, SymbolSize);
StrTab->add(RelocName);
}
// Copy the fixup information from per-function Assembler memory to the
// object writer's memory, for writing later.
......
......@@ -44,8 +44,18 @@
namespace std {
template <> struct hash<Ice::RelocatableTuple> {
size_t operator()(const Ice::RelocatableTuple &Key) const {
if (!Key.EmitString.empty()) {
return hash<Ice::IceString>()(Key.EmitString);
}
assert(!Key.OffsetExpr.empty());
if (Key.OffsetExpr[0]->hasOffset()) {
return hash<Ice::IceString>()(Key.Name) +
hash<Ice::RelocOffsetT>()(Key.OffsetExpr[0]->getOffset());
}
return hash<Ice::IceString>()(Key.Name) +
hash<Ice::RelocOffsetT>()(Key.Offset);
hash<std::size_t>()(Key.OffsetExpr.size());
}
};
} // end of namespace std
......@@ -785,18 +795,28 @@ Constant *GlobalContext::getConstantDouble(double ConstantDouble) {
return getConstPool()->Doubles.getOrAdd(this, ConstantDouble);
}
Constant *GlobalContext::getConstantSym(RelocOffsetT Offset,
Constant *GlobalContext::getConstantSym(const RelocOffsetArray &Offset,
const IceString &Name,
const IceString &EmitString,
bool SuppressMangling) {
return getConstPool()->Relocatables.getOrAdd(
this, RelocatableTuple(Offset, Name, SuppressMangling));
this, RelocatableTuple(Offset, Name, EmitString, SuppressMangling));
}
Constant *GlobalContext::getConstantSym(RelocOffsetT Offset,
const IceString &Name,
bool SuppressMangling) {
constexpr char EmptyEmitString[] = "";
return getConstantSym({RelocOffset::create(this, Offset)}, Name,
EmptyEmitString, SuppressMangling);
}
Constant *GlobalContext::getConstantExternSym(const IceString &Name) {
constexpr RelocOffsetT Offset = 0;
constexpr bool SuppressMangling = true;
return getConstPool()->ExternRelocatables.getOrAdd(
this, RelocatableTuple(Offset, Name, SuppressMangling));
this, RelocatableTuple({RelocOffset::create(this, Offset)}, Name,
SuppressMangling));
}
Constant *GlobalContext::getConstantUndef(Type Ty) {
......
......@@ -200,6 +200,9 @@ public:
Constant *getConstantFloat(float Value);
Constant *getConstantDouble(double Value);
/// Returns a symbolic constant.
Constant *getConstantSym(const RelocOffsetArray &Offset,
const IceString &Name, const IceString &EmitString,
bool SuppressMangling);
Constant *getConstantSym(RelocOffsetT Offset, const IceString &Name,
bool SuppressMangling);
Constant *getConstantExternSym(const IceString &Name);
......
......@@ -337,13 +337,13 @@ template <typename TraitsType> struct InstImpl {
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
void setIsReturnLocation(bool Value) { IsReturnLocation = Value; }
void setRelocOffset(RelocOffset *Value) { OffsetReloc = Value; }
private:
InstX86Label(Cfg *Func, TargetLowering *Target);
SizeT Number; // used for unique label generation.
bool IsReturnLocation = false;
RelocOffset *OffsetReloc = nullptr;
};
/// Conditional and unconditional branch instruction.
......
......@@ -459,10 +459,10 @@ void InstImpl<TraitsType>::InstX86Label::emit(const Cfg *Func) const {
template <typename TraitsType>
void InstImpl<TraitsType>::InstX86Label::emitIAS(const Cfg *Func) const {
Assembler *Asm = Func->getAssembler<Assembler>();
if (IsReturnLocation) {
Asm->addRelocationAtCurrentPosition(getName(Func));
}
Asm->bindLocalLabel(Number);
if (OffsetReloc != nullptr) {
Asm->bindRelocOffset(OffsetReloc);
}
}
template <typename TraitsType>
......
......@@ -24,7 +24,66 @@
namespace Ice {
bool operator==(const RelocatableTuple &A, const RelocatableTuple &B) {
return A.Offset == B.Offset && A.Name == B.Name;
// A and B are the same if:
// (1) they have the same name; and
// (2) they have the same offset.
//
// (1) is trivial to check, but (2) requires some care.
//
// For (2):
// if A and B have known offsets (i.e., no symbolic references), then
// A == B -> A.Offset == B.Offset.
// else each element i in A.OffsetExpr[i] must be the same (or have the same
// value) as B.OffsetExpr[i].
if (A.Name != B.Name) {
return false;
}
bool BothHaveKnownOffsets = true;
RelocOffsetT OffsetA = 0;
RelocOffsetT OffsetB = 0;
for (SizeT i = 0; i < A.OffsetExpr.size() && BothHaveKnownOffsets; ++i) {
BothHaveKnownOffsets = A.OffsetExpr[i]->hasOffset();
if (BothHaveKnownOffsets) {
OffsetA += A.OffsetExpr[i]->getOffset();
}
}
for (SizeT i = 0; i < B.OffsetExpr.size() && BothHaveKnownOffsets; ++i) {
BothHaveKnownOffsets = B.OffsetExpr[i]->hasOffset();
if (BothHaveKnownOffsets) {
OffsetB += B.OffsetExpr[i]->getOffset();
}
}
if (BothHaveKnownOffsets) {
// Both have known offsets (i.e., no unresolved symbolic references), so
// A == B -> A.Offset == B.Offset.
return OffsetA == OffsetB;
}
// Otherwise, A and B are not the same if their OffsetExpr's have different
// sizes.
if (A.OffsetExpr.size() != B.OffsetExpr.size()) {
return false;
}
// If the OffsetExprs' sizes are the same, then
// for each i in OffsetExprSize:
for (SizeT i = 0; i < A.OffsetExpr.size(); ++i) {
const auto *const RelocOffsetA = A.OffsetExpr[i];
const auto *const RelocOffsetB = B.OffsetExpr[i];
if (RelocOffsetA->hasOffset() && RelocOffsetB->hasOffset()) {
// A.OffsetExpr[i].Offset == B.OffsetExpr[i].Offset iff they are both
// defined;
if (RelocOffsetA->getOffset() != RelocOffsetB->getOffset()) {
return false;
}
} else if (RelocOffsetA != RelocOffsetB) {
// or, if they are undefined, then the RelocOffsets must be the same.
return false;
}
}
return true;
}
bool operator<(const RegWeight &A, const RegWeight &B) {
......@@ -501,8 +560,13 @@ void ConstantRelocatable::dump(const Cfg *Func, Ostream &Str) const {
} else {
Str << Name;
}
if (Offset)
Str << "+" << Offset;
const RelocOffsetT Offset = getOffset();
if (Offset) {
if (Offset >= 0) {
Str << "+";
}
Str << Offset;
}
}
void ConstantUndef::emit(TargetLowering *Target) const { Target->emit(this); }
......
......@@ -26,6 +26,8 @@
#include "llvm/Support/Format.h"
#include <limits>
namespace Ice {
class Operand {
......@@ -247,6 +249,43 @@ inline void ConstantInteger64::dump(const Cfg *, Ostream &Str) const {
Str << static_cast<int64_t>(getValue());
}
/// RelocOffset allows symbolic references in ConstantRelocatables' offsets,
/// e.g., 8 + LabelOffset, where label offset is the location (code or data)
/// of a Label that is only determinable during ELF emission.
class RelocOffset final {
RelocOffset(const RelocOffset &) = delete;
RelocOffset &operator=(const RelocOffset &) = delete;
public:
static RelocOffset *create(GlobalContext *Ctx) {
return new (Ctx->allocate<RelocOffset>()) RelocOffset();
}
static RelocOffset *create(GlobalContext *Ctx, RelocOffsetT Value) {
return new (Ctx->allocate<RelocOffset>()) RelocOffset(Value);
}
bool hasOffset() const { return HasOffset; }
RelocOffsetT getOffset() const {
assert(HasOffset);
return Offset;
}
void setOffset(const RelocOffsetT Value) {
assert(!HasOffset);
Offset = Value;
HasOffset = true;
}
private:
RelocOffset() = default;
explicit RelocOffset(RelocOffsetT Offset) { setOffset(Offset); }
bool HasOffset = false;
RelocOffsetT Offset;
};
/// RelocatableTuple bundles the parameters that are used to construct an
/// ConstantRelocatable. It is done this way so that ConstantRelocatable can fit
/// into the global constant pool template mechanism.
......@@ -255,14 +294,22 @@ class RelocatableTuple {
RelocatableTuple &operator=(const RelocatableTuple &) = delete;
public:
RelocatableTuple(const RelocOffsetT Offset, const IceString &Name,
RelocatableTuple(const RelocOffsetArray &OffsetExpr, const IceString &Name,
bool SuppressMangling)
: Offset(Offset), Name(Name), SuppressMangling(SuppressMangling) {}
: OffsetExpr(OffsetExpr), Name(Name), SuppressMangling(SuppressMangling) {
}
RelocatableTuple(const RelocOffsetArray &OffsetExpr, const IceString &Name,
const IceString &EmitString, bool SuppressMangling)
: OffsetExpr(OffsetExpr), Name(Name), EmitString(EmitString),
SuppressMangling(SuppressMangling) {}
RelocatableTuple(const RelocatableTuple &) = default;
const RelocOffsetT Offset;
const RelocOffsetArray OffsetExpr;
const IceString Name;
bool SuppressMangling;
const IceString EmitString;
const bool SuppressMangling;
};
bool operator==(const RelocatableTuple &A, const RelocatableTuple &B);
......@@ -277,13 +324,22 @@ class ConstantRelocatable : public Constant {
public:
static ConstantRelocatable *create(GlobalContext *Ctx, Type Ty,
const RelocatableTuple &Tuple) {
return new (Ctx->allocate<ConstantRelocatable>()) ConstantRelocatable(
Ty, Tuple.Offset, Tuple.Name, Tuple.SuppressMangling);
return new (Ctx->allocate<ConstantRelocatable>())
ConstantRelocatable(Ty, Tuple.OffsetExpr, Tuple.Name, Tuple.EmitString,
Tuple.SuppressMangling);
}
RelocOffsetT getOffset() const { return Offset; }
RelocOffsetT getOffset() const {
RelocOffsetT Offset = 0;
for (const auto *const OffsetReloc : OffsetExpr) {
Offset += OffsetReloc->getOffset();
}
return Offset;
}
const IceString &getEmitString() const { return EmitString; }
const IceString &getName() const { return Name; }
void setSuppressMangling(bool Value) { SuppressMangling = Value; }
bool getSuppressMangling() const { return SuppressMangling; }
using Constant::emit;
void emit(TargetLowering *Target) const final;
......@@ -298,13 +354,16 @@ public:
}
private:
ConstantRelocatable(Type Ty, RelocOffsetT Offset, const IceString &Name,
ConstantRelocatable(Type Ty, const RelocOffsetArray &OffsetExpr,
const IceString &Name, const IceString &EmitString,
bool SuppressMangling)
: Constant(kConstRelocatable, Ty), Offset(Offset), Name(Name),
SuppressMangling(SuppressMangling) {}
const RelocOffsetT Offset; /// fixed offset to add
const IceString Name; /// optional for debug/dump
bool SuppressMangling;
: Constant(kConstRelocatable, Ty), OffsetExpr(OffsetExpr), Name(Name),
EmitString(EmitString), SuppressMangling(SuppressMangling) {}
const RelocOffsetArray OffsetExpr; /// fixed offset to add
const IceString Name; /// optional for debug/dump
const IceString EmitString; /// optional for textual emission
const bool SuppressMangling;
};
/// ConstantUndef represents an unspecified bit pattern. Although it is legal to
......
......@@ -704,6 +704,13 @@ void TargetLowering::emitWithoutPrefix(const ConstantRelocatable *C,
if (!BuildDefs::dump())
return;
Ostream &Str = Ctx->getStrEmit();
const IceString &EmitStr = C->getEmitString();
if (!EmitStr.empty()) {
// C has a custom emit string, so we use it instead of the canonical
// Name + Offset form.
Str << EmitStr;
return;
}
if (C->getSuppressMangling())
Str << C->getName();
else
......
......@@ -598,15 +598,44 @@ Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) {
Inst *NewCall = nullptr;
auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget);
if (NeedSandboxing) {
// In NaCl sandbox, calls are replaced by a push/jmp pair:
//
// push .after_call
// jmp CallTarget
// .align bundle_size
// after_call:
//
// In order to emit this sequence, we need a temporary label ("after_call",
// in this example.)
//
// The operand to push is a ConstantRelocatable. The easy way to implement
// this sequence is to create a ConstantRelocatable(0, "after_call"), but
// this ends up creating more relocations for the linker to resolve.
// Therefore, we create a ConstantRelocatable from the name of the function
// being compiled (i.e., ConstantRelocatable(after_call - Func, Func).
//
// By default, ConstantRelocatables are emitted (in textual output) as
//
// ConstantName + Offset
//
// ReturnReloc has an offset that is only known during binary emission.
// Therefore, we set a custom emit string for ReturnReloc that will be
// used instead. In this particular case, the code will be emitted as
//
// push .after_call
InstX86Label *ReturnAddress = InstX86Label::create(Func, this);
ReturnAddress->setIsReturnLocation(true);
auto *ReturnRelocOffset = RelocOffset::create(Ctx);
ReturnAddress->setRelocOffset(ReturnRelocOffset);
constexpr bool SuppressMangling = true;
const IceString EmitString = ReturnAddress->getName(Func);
auto *ReturnReloc = llvm::cast<ConstantRelocatable>(Ctx->getConstantSym(
{ReturnRelocOffset}, Ctx->mangleName(Func->getFunctionName()),
EmitString, SuppressMangling));
/* AutoBundle scoping */ {
std::unique_ptr<AutoBundle> Bundler;
if (CallTargetR == nullptr) {
Bundler = makeUnique<AutoBundle>(this, InstBundleLock::Opt_PadToEnd);
_push(Ctx->getConstantSym(0, ReturnAddress->getName(Func),
SuppressMangling));
_push(ReturnReloc);
} else {
Variable *T = makeReg(IceType_i32);
Variable *T64 = makeReg(IceType_i64);
......@@ -615,8 +644,7 @@ Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) {
_mov(T, CallTargetR);
Bundler = makeUnique<AutoBundle>(this, InstBundleLock::Opt_PadToEnd);
_push(Ctx->getConstantSym(0, ReturnAddress->getName(Func),
SuppressMangling));
_push(ReturnReloc);
const SizeT BundleSize =
1 << Func->getAssembler<>()->getBundleAlignLog2Bytes();
_and(T, Ctx->getConstantInt32(~(BundleSize - 1)));
......
......@@ -74,7 +74,7 @@ struct TargetX8664Traits {
RegX8664::Encoded_Reg_eax;
static constexpr GPRRegister Encoded_Reg_Counter = RegX8664::Encoded_Reg_ecx;
static constexpr FixupKind FK_PcRel = llvm::ELF::R_X86_64_PC32;
static constexpr FixupKind FK_Abs = llvm::ELF::R_X86_64_32;
static constexpr FixupKind FK_Abs = llvm::ELF::R_X86_64_32S;
static constexpr FixupKind FK_Gotoff = llvm::ELF::R_X86_64_GOTOFF64;
static constexpr FixupKind FK_GotPC = llvm::ELF::R_X86_64_GOTPC32;
......
......@@ -28,9 +28,8 @@ entry:
; CHECK: 1b: {{.*}} call 1c
; CHECK-NEXT: 20:
; X8664-LABEL: test_direct_call
; X8664: push {{.*}}$local$__0
; X8664: push {{.*}} R_X86_64_32S test_direct_call+{{.*}}20
; X8664: jmp {{.*}} call_target
; X8664: {{0+}}20 <{{.*}}$local$__0>:
; An indirect call sequence uses the right mask and register-call sequence.
define internal void @test_indirect_call(i32 %target) {
......@@ -46,11 +45,10 @@ entry:
; CHECK-NEXT: call [[REG]]
; CHECk-NEXT: 20:
; X8664-LABEL: test_indirect_call
; X8664: push {{.*}}$local$__0
; X8664: push {{.*}} R_X86_64_32S test_indirect_call+{{.*}}20
; X8664: {{.*}} and e[[REG:..]],0xffffffe0
; X8664: add r[[REG]],r15
; X8664: jmp r[[REG]]
; X8664: {{0+}}20 <{{.*}}$local$__0>:
; A return sequence uses the right pop / mask / jmp sequence.
define internal void @test_ret() {
......@@ -271,8 +269,7 @@ define internal void @bundle_lock_pad_to_end_padding_0(i32 %arg0, i32 %arg1,
; bundle boundary
ret void
}
; X8664-LABEL: bundle_lock_pad_to_end_padding_0$local$__0
; X8664: 56: {{.*}} push {{.*}}$local$__1
; X8664: 56: {{.*}} push {{.*}} R_X86_64_32S bundle_lock_pad_to_end_padding_0+{{.*}}60
; X8664: 5b: {{.*}} jmp {{.*}} call_target
; X8664: 60: {{.*}} add
......@@ -289,8 +286,7 @@ define internal void @bundle_lock_pad_to_end_padding_11(i32 %arg0, i32 %arg1,
; bundle boundary
ret void
}
; X8664-LABEL: bundle_lock_pad_to_end_padding_11$local$__0
; X8664: 4b: {{.*}} push {{.*}}$local$__1
; X8664: 4b: {{.*}} push {{.*}} R_X86_64_32S bundle_lock_pad_to_end_padding_11+{{.*}}60
; X8664: 50: {{.*}} jmp {{.*}} call_target
; X8664: 55: {{.*}} nop
; X8664: 5d: {{.*}} nop
......@@ -308,8 +304,7 @@ define internal void @bundle_lock_pad_to_end_padding_22(i32 %arg0, i32 %arg1,
; bundle boundary
ret void
}
; X8664-LABEL: bundle_lock_pad_to_end_padding_22$local$__0
; X8664: 40: {{.*}} push {{.*}}$local$__1
; X8664: 40: {{.*}} push {{.*}} R_X86_64_32S bundle_lock_pad_to_end_padding_22+{{.*}}60
; X8664: 45: {{.*}} jmp {{.*}} call_target
; X8664: 4a: {{.*}} nop
; X8664: 52: {{.*}} nop
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment