Commit 5403f5dc by Karl Schimpf

Implements include/exclude register lists for translation.

This allows better debugging of register encodings into instructions, in the integrated assembler. BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4334 R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1571433004 .
parent 6f7ad6c2
......@@ -51,7 +51,7 @@ public:
uint32_t getSequenceNumber() const { return SequenceNumber; }
static constexpr VerboseMask defaultVerboseMask() {
return IceV_All & ~IceV_Status;
return IceV_All & ~IceV_Status & ~IceV_AvailableRegs;
}
/// Returns true if any of the specified options in the verbose mask are set.
/// If the argument is omitted, it checks if any verbose options at all are
......
......@@ -293,6 +293,17 @@ cl::opt<int> NopProbabilityAsPercentage(
"nop-insertion-percentage",
cl::desc("Nop insertion probability as percentage"), cl::init(10));
/// Restricts registers in corresponding register classes to specified list.
cl::list<std::string> UseRestrictedRegisters(
"reg-use", cl::CommaSeparated,
cl::desc(
"Only use specified registers for corresponding register classes"));
/// List of excluded registers.
cl::list<std::string>
ExcludedRegisters("reg-exclude", cl::CommaSeparated,
cl::desc("Don't use specified registers"));
/// Verbose options (can be comma-separated).
cl::list<Ice::VerboseItem> VerboseList(
"verbose", cl::CommaSeparated,
......@@ -315,6 +326,8 @@ cl::list<Ice::VerboseItem> VerboseList(
clEnumValN(Ice::IceV_Loop, "loop", "Loop nest depth analysis"),
clEnumValN(Ice::IceV_Status, "status",
"Print the name of the function being translated"),
clEnumValN(Ice::IceV_AvailableRegs, "registers",
"Show available registers for register allocation"),
clEnumValN(Ice::IceV_All, "all", "Use all verbose options"),
clEnumValN(Ice::IceV_Most, "most",
"Use all verbose options except 'regalloc'"),
......@@ -487,6 +500,9 @@ void ClFlags::resetClFlags(ClFlags &OutFlags) {
// size_t and 64-bit fields.
OutFlags.NumTranslationThreads = 0;
OutFlags.RandomSeed = 0;
// Unordered set fields.
OutFlags.clearExcludedRegisters();
OutFlags.clearUseRestrictedRegisters();
}
void ClFlags::getParsedClFlags(ClFlags &OutFlags) {
......@@ -513,6 +529,7 @@ void ClFlags::getParsedClFlags(ClFlags &OutFlags) {
OutFlags.setDisableTranslation(::DisableTranslation);
OutFlags.setDumpStats(::DumpStats);
OutFlags.setEnableBlockProfile(::EnableBlockProfile);
OutFlags.setExcludedRegisters(::ExcludedRegisters);
OutFlags.setForceMemIntrinOpt(::ForceMemIntrinOpt);
OutFlags.setFunctionSections(::FunctionSections);
OutFlags.setNumTranslationThreads(::NumThreads);
......@@ -542,6 +559,7 @@ void ClFlags::getParsedClFlags(ClFlags &OutFlags) {
OutFlags.setTimingFocusOn(::TimingFocusOn);
OutFlags.setTranslateOnly(::TranslateOnly);
OutFlags.setUseNonsfi(::UseNonsfi);
OutFlags.setUseRestrictedRegisters(::UseRestrictedRegisters);
OutFlags.setUseSandboxing(::UseSandboxing);
OutFlags.setVerboseFocusOn(::VerboseFocusOn);
OutFlags.setOutFileType(::OutFileType);
......
......@@ -28,6 +28,8 @@ class ClFlags {
ClFlags &operator=(const ClFlags &) = delete;
public:
using StringVector = std::vector<IceString>;
/// User defined constructor.
ClFlags() { resetClFlags(*this); }
......@@ -115,6 +117,16 @@ public:
/// Set ClFlags::EnableBlockProfile to a new value
void setEnableBlockProfile(bool NewValue) { EnableBlockProfile = NewValue; }
/// Get the restricted list of registers to use, for corresponding register
/// classes, in register allocation.
const StringVector &getUseRestrictedRegisters() const {
return UseRestrictedRegisters;
}
void clearUseRestrictedRegisters() { UseRestrictedRegisters.clear(); }
void setUseRestrictedRegisters(const StringVector &Registers) {
UseRestrictedRegisters = Registers;
}
/// Get the value of ClFlags::ForceMemIntrinOpt
bool getForceMemIntrinOpt() const { return ForceMemIntrinOpt; }
/// Set ClFlags::ForceMemIntrinOpt to a new value
......@@ -186,6 +198,13 @@ public:
/// Set ClFlags::UseNonsfi to a new value
void setUseNonsfi(bool NewValue) { UseNonsfi = NewValue; }
/// Get the list of registers exluded in register allocation.
const StringVector &getExcludedRegisters() const { return ExcludedRegisters; }
void clearExcludedRegisters() { ExcludedRegisters.clear(); }
void setExcludedRegisters(const StringVector &Registers) {
ExcludedRegisters = Registers;
}
/// Get the value of ClFlags::UseSandboxing
bool getUseSandboxing() const { return UseSandboxing; }
/// Set ClFlags::UseSandboxing to a new value
......@@ -390,6 +409,8 @@ private:
bool DumpStats;
/// see anonymous_namespace{IceClFlags.cpp}::EnableBlockProfile
bool EnableBlockProfile;
/// see anonymous_namespace{IceClFlags.cpp}::ExcludedRegisters;
StringVector ExcludedRegisters;
/// see anonymous_namespace{IceClFlags.cpp}::ForceMemIntrinOpt
bool ForceMemIntrinOpt;
/// see anonymous_namespace{IceClFlags.cpp}::FunctionSections
......@@ -422,6 +443,8 @@ private:
bool TimeEachFunction;
/// see anonymous_namespace{IceClFlags.cpp}::UseNonsfi
bool UseNonsfi;
/// see anonymous_namespace{IceClFlags.cpp}::UseRegistrictedRegisters;
StringVector UseRestrictedRegisters;
/// see anonymous_namespace{IceClFlags.cpp}::UseSandboxing
bool UseSandboxing;
/// see anonymous_namespace{IceClFlags.cpp}::OLevel
......
......@@ -242,6 +242,7 @@ enum VerboseItem {
IceV_RMW = 1 << 12,
IceV_Loop = 1 << 13,
IceV_Status = 1 << 14,
IceV_AvailableRegs = 1 << 15,
IceV_All = ~IceV_None,
IceV_Most = IceV_All & ~IceV_LinearScan
};
......
......@@ -277,7 +277,7 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
ProfileBlockInfoVarDecl->setSuppressMangling();
ProfileBlockInfoVarDecl->setLinkage(llvm::GlobalValue::ExternalLinkage);
TargetLowering::staticInit(Flags);
TargetLowering::staticInit(this);
}
void GlobalContext::translateFunctions() {
......
......@@ -58,6 +58,8 @@ static inline GPRRegister getEncodedGPR(int32_t RegNum) {
return GPRRegister(RegNum - Reg_GPR_First);
}
const char *getRegName(int32_t RegNum);
} // end of namespace RegMIPS32
// Extend enum RegClass with MIPS32-specific register classes (if any).
......
......@@ -39,7 +39,7 @@
// createTargetDataLowering(Ice::GlobalContext*);
// unique_ptr<Ice::TargetHeaderLowering>
// createTargetHeaderLowering(Ice::GlobalContext *);
// void staticInit(const ::Ice::ClFlags &Flags);
// void staticInit(::Ice::GlobalContext *);
// }
#define SUBZERO_TARGET(X) \
namespace X { \
......@@ -49,7 +49,7 @@
createTargetDataLowering(::Ice::GlobalContext *Ctx); \
std::unique_ptr<::Ice::TargetHeaderLowering> \
createTargetHeaderLowering(::Ice::GlobalContext *Ctx); \
void staticInit(const ::Ice::ClFlags &Flags); \
void staticInit(::Ice::GlobalContext *Ctx); \
} // end of namespace X
#include "llvm/Config/SZTargets.def"
#undef SUBZERO_TARGET
......@@ -116,6 +116,102 @@ Variable *LoweringContext::availabilityGet(Operand *Src) const {
return nullptr;
}
namespace {
void printRegisterSet(Ostream &Str, const llvm::SmallBitVector &Bitset,
std::function<IceString(int32_t)> getRegName,
const IceString &LineIndentString) {
constexpr size_t RegistersPerLine = 16;
size_t Count = 0;
for (int i = Bitset.find_first(); i != -1; i = Bitset.find_next(i)) {
if (Count == 0) {
Str << LineIndentString;
} else {
Str << ",";
}
if (Count > 0 && Count % RegistersPerLine == 0)
Str << "\n" << LineIndentString;
++Count;
Str << getRegName(i);
}
if (Count)
Str << "\n";
}
} // end of anonymous namespace
void TargetLowering::filterTypeToRegisterSet(
GlobalContext *Ctx, int32_t NumRegs,
llvm::SmallBitVector TypeToRegisterSet[], size_t TypeToRegisterSetSize,
std::function<IceString(int32_t)> getRegName) {
llvm::SmallBitVector ExcludeBitSet(NumRegs);
std::vector<llvm::SmallBitVector> UseSet(TypeToRegisterSetSize,
ExcludeBitSet);
ExcludeBitSet.flip();
std::unordered_map<IceString, int32_t> RegNameToIndex;
for (int32_t RegIndex = 0; RegIndex < NumRegs; ++RegIndex)
RegNameToIndex[getRegName(RegIndex)] = RegIndex;
ClFlags::StringVector BadRegNames;
for (const IceString &RegName : Ctx->getFlags().getUseRestrictedRegisters()) {
if (!RegNameToIndex.count(RegName)) {
BadRegNames.push_back(RegName);
continue;
}
const int32_t RegIndex = RegNameToIndex[RegName];
for (SizeT TypeIndex = 0; TypeIndex < TypeToRegisterSetSize; ++TypeIndex)
UseSet[TypeIndex][RegIndex] = TypeToRegisterSet[TypeIndex][RegIndex];
}
for (const IceString &RegName : Ctx->getFlags().getExcludedRegisters()) {
if (!RegNameToIndex.count(RegName)) {
BadRegNames.push_back(RegName);
continue;
}
ExcludeBitSet[RegNameToIndex[RegName]] = false;
}
if (!BadRegNames.empty()) {
std::string Buffer;
llvm::raw_string_ostream StrBuf(Buffer);
StrBuf << "Unrecognized use/exclude registers:";
for (const auto &RegName : BadRegNames)
StrBuf << " " << RegName;
llvm::report_fatal_error(StrBuf.str());
}
// Apply filters.
for (size_t TypeIndex = 0; TypeIndex < TypeToRegisterSetSize; ++TypeIndex) {
llvm::SmallBitVector *TypeBitSet = &TypeToRegisterSet[TypeIndex];
llvm::SmallBitVector *UseBitSet = &UseSet[TypeIndex];
if (UseBitSet->any())
*TypeBitSet = *UseBitSet;
*TypeBitSet &= ExcludeBitSet;
}
// Display filtered register sets, if requested.
if (BuildDefs::dump() && NumRegs &&
(Ctx->getFlags().getVerbose() & IceV_AvailableRegs)) {
Ostream &Str = Ctx->getStrDump();
const IceString Indent = " ";
const IceString IndentTwice = Indent + Indent;
Str << "Registers available for register allocation:\n";
for (size_t TypeIndex = 0; TypeIndex < TypeToRegisterSetSize; ++TypeIndex) {
Str << Indent;
if (TypeIndex < IceType_NUM) {
Str << typeString(static_cast<Type>(TypeIndex));
} else {
Str << "other[" << TypeIndex << "]";
}
Str << ":\n";
printRegisterSet(Str, TypeToRegisterSet[TypeIndex], getRegName,
IndentTwice);
}
Str << "\n";
}
}
std::unique_ptr<TargetLowering>
TargetLowering::createLowering(TargetArch Target, Cfg *Func) {
switch (Target) {
......@@ -129,8 +225,8 @@ TargetLowering::createLowering(TargetArch Target, Cfg *Func) {
}
}
void TargetLowering::staticInit(const ClFlags &Flags) {
const TargetArch Target = Flags.getTargetArch();
void TargetLowering::staticInit(GlobalContext *Ctx) {
const TargetArch Target = Ctx->getFlags().getTargetArch();
// Call the specified target's static initializer.
switch (Target) {
default:
......@@ -142,7 +238,7 @@ void TargetLowering::staticInit(const ClFlags &Flags) {
return; \
} \
InitGuard##X = true; \
::X::staticInit(Flags); \
::X::staticInit(Ctx); \
} break;
#include "llvm/Config/SZTargets.def"
#undef SUBZERO_TARGET
......
......@@ -152,9 +152,9 @@ class TargetLowering {
TargetLowering &operator=(const TargetLowering &) = delete;
public:
static void staticInit(const ClFlags &Flags);
static void staticInit(GlobalContext *Ctx);
// Each target must define a public static method:
// static void staticInit(const ClFlags &Flags);
// static void staticInit(GlobalContext *Ctx);
static std::unique_ptr<TargetLowering> createLowering(TargetArch Target,
Cfg *Func);
......@@ -299,6 +299,12 @@ public:
protected:
explicit TargetLowering(Cfg *Func);
// Applies command line filters to TypeToRegisterSet array.
static void
filterTypeToRegisterSet(GlobalContext *Ctx, int32_t NumRegs,
llvm::SmallBitVector TypeToRegisterSet[],
size_t TypeToRegisterSetSize,
std::function<IceString(int32_t)> getRegName);
virtual void lowerAlloca(const InstAlloca *Inst) = 0;
virtual void lowerArithmetic(const InstArithmetic *Inst) = 0;
virtual void lowerAssign(const InstAssign *Inst) = 0;
......
......@@ -50,8 +50,8 @@ createTargetHeaderLowering(::Ice::GlobalContext *Ctx) {
return ::Ice::ARM32::TargetHeaderARM32::create(Ctx);
}
void staticInit(const ::Ice::ClFlags &Flags) {
::Ice::ARM32::TargetARM32::staticInit(Flags);
void staticInit(::Ice::GlobalContext *Ctx) {
::Ice::ARM32::TargetARM32::staticInit(Ctx);
}
} // end of namespace ARM32
......@@ -277,8 +277,8 @@ TargetARM32::TargetARM32(Cfg *Func)
: TargetLowering(Func), NeedSandboxing(Ctx->getFlags().getUseSandboxing()),
CPUFeatures(Func->getContext()->getFlags()) {}
void TargetARM32::staticInit(const ClFlags &Flags) {
(void)Flags;
void TargetARM32::staticInit(GlobalContext *Ctx) {
// Limit this size (or do all bitsets need to be the same width)???
llvm::SmallBitVector IntegerRegisters(RegARM32::Reg_NUM);
llvm::SmallBitVector I64PairRegisters(RegARM32::Reg_NUM);
......@@ -331,6 +331,20 @@ void TargetARM32::staticInit(const ClFlags &Flags) {
TypeToRegisterSet[IceType_v8i16] = VectorRegisters;
TypeToRegisterSet[IceType_v4i32] = VectorRegisters;
TypeToRegisterSet[IceType_v4f32] = VectorRegisters;
filterTypeToRegisterSet(
Ctx, RegARM32::Reg_NUM, TypeToRegisterSet, RegARM32::RCARM32_NUM,
[](int32_t RegNum) -> IceString {
IceString Name = RegARM32::getRegName(RegNum);
constexpr const char RegSeparator[] = ", ";
constexpr size_t RegSeparatorWidth =
llvm::array_lengthof(RegSeparator) - 1;
for (size_t Pos = Name.find(RegSeparator); Pos != std::string::npos;
Pos = Name.find(RegSeparator)) {
Name.replace(Pos, RegSeparatorWidth, ":");
}
return Name;
});
}
namespace {
......@@ -1874,7 +1888,7 @@ llvm::SmallBitVector TargetARM32::getRegisterSet(RegSetMask Include,
RegSetMask Exclude) const {
llvm::SmallBitVector Registers(RegARM32::Reg_NUM);
for (int i = 0; i < RegARM32::Reg_NUM; ++i) {
for (int32_t i = 0; i < RegARM32::Reg_NUM; ++i) {
const auto &Entry = RegARM32::RegTable[i];
if (Entry.Scratch && (Include & RegSet_CallerSave))
Registers[i] = true;
......
......@@ -57,7 +57,7 @@ class TargetARM32 : public TargetLowering {
TargetARM32 &operator=(const TargetARM32 &) = delete;
public:
static void staticInit(const ClFlags &Flags);
static void staticInit(GlobalContext *Ctx);
// TODO(jvoung): return a unique_ptr.
static std::unique_ptr<::Ice::TargetLowering> create(Cfg *Func) {
return makeUnique<TargetARM32>(Func);
......
......@@ -45,8 +45,8 @@ createTargetHeaderLowering(::Ice::GlobalContext *Ctx) {
return ::Ice::MIPS32::TargetHeaderMIPS32::create(Ctx);
}
void staticInit(const ::Ice::ClFlags &Flags) {
::Ice::MIPS32::TargetMIPS32::staticInit(Flags);
void staticInit(::Ice::GlobalContext *Ctx) {
::Ice::MIPS32::TargetMIPS32::staticInit(Ctx);
}
} // end of namespace MIPS32
......@@ -64,8 +64,8 @@ constexpr uint32_t MIPS32_MAX_GPR_ARG = 4;
TargetMIPS32::TargetMIPS32(Cfg *Func) : TargetLowering(Func) {}
void TargetMIPS32::staticInit(const ClFlags &Flags) {
(void)Flags;
void TargetMIPS32::staticInit(GlobalContext *Ctx) {
(void)Ctx;
llvm::SmallBitVector IntegerRegisters(RegMIPS32::Reg_NUM);
llvm::SmallBitVector I64PairRegisters(RegMIPS32::Reg_NUM);
llvm::SmallBitVector Float32Registers(RegMIPS32::Reg_NUM);
......@@ -106,6 +106,11 @@ void TargetMIPS32::staticInit(const ClFlags &Flags) {
TypeToRegisterSet[IceType_v8i16] = VectorRegisters;
TypeToRegisterSet[IceType_v4i32] = VectorRegisters;
TypeToRegisterSet[IceType_v4f32] = VectorRegisters;
filterTypeToRegisterSet(Ctx, RegMIPS32::Reg_NUM, TypeToRegisterSet,
RCMIPS32_NUM, [](int32_t RegNum) -> IceString {
return RegMIPS32::getRegName(RegNum);
});
}
void TargetMIPS32::translateO2() {
......@@ -262,19 +267,28 @@ bool TargetMIPS32::doBranchOpt(Inst *I, const CfgNode *NextNode) {
return false;
}
IceString TargetMIPS32::getRegName(SizeT RegNum, Type Ty) const {
assert(RegNum < RegMIPS32::Reg_NUM);
(void)Ty;
static const char *RegNames[] = {
namespace {
const char *RegNames[RegMIPS32::Reg_NUM] = {
#define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isI64Pair, isFP32, isFP64, isVec128, alias_init) \
name,
REGMIPS32_TABLE
REGMIPS32_TABLE
#undef X
};
};
} // end of anonymous namespace
const char *RegMIPS32::getRegName(int32_t RegNum) {
assert(RegNum < RegMIPS32::Reg_NUM);
return RegNames[RegNum];
}
IceString TargetMIPS32::getRegName(SizeT RegNum, Type Ty) const {
(void)Ty;
return RegMIPS32::getRegName(RegNum);
}
Variable *TargetMIPS32::getPhysicalRegister(SizeT RegNum, Type Ty) {
if (Ty == IceType_void)
Ty = IceType_i32;
......
......@@ -33,7 +33,7 @@ class TargetMIPS32 : public TargetLowering {
public:
~TargetMIPS32() override = default;
static void staticInit(const ClFlags &Flags);
static void staticInit(GlobalContext *Ctx);
static std::unique_ptr<::Ice::TargetLowering> create(Cfg *Func) {
return makeUnique<TargetMIPS32>(Func);
}
......
......@@ -32,8 +32,8 @@ createTargetHeaderLowering(::Ice::GlobalContext *Ctx) {
return ::Ice::X8632::TargetHeaderX8632::create(Ctx);
}
void staticInit(const ::Ice::ClFlags &Flags) {
::Ice::X8632::TargetX8632::staticInit(Flags);
void staticInit(::Ice::GlobalContext *Ctx) {
::Ice::X8632::TargetX8632::staticInit(Ctx);
}
} // end of namespace X8632
......
......@@ -32,8 +32,8 @@ createTargetHeaderLowering(::Ice::GlobalContext *Ctx) {
return ::Ice::X8664::TargetHeaderX8664::create(Ctx);
}
void staticInit(const ::Ice::ClFlags &Flags) {
::Ice::X8664::TargetX8664::staticInit(Flags);
void staticInit(::Ice::GlobalContext *Ctx) {
::Ice::X8664::TargetX8664::staticInit(Ctx);
}
} // end of namespace X8664
......
......@@ -78,7 +78,7 @@ public:
~TargetX86Base() override = default;
static void staticInit(const ClFlags &Flags);
static void staticInit(GlobalContext *Ctx);
static TargetX86Base *create(Cfg *Func) { return new TargetX86Base(Func); }
static FixupKind getPcRelFixup() { return PcRelFixup; }
......
......@@ -324,11 +324,15 @@ TargetX86Base<TraitsType>::TargetX86Base(Cfg *Func)
}
template <typename TraitsType>
void TargetX86Base<TraitsType>::staticInit(const ClFlags &Flags) {
Traits::initRegisterSet(Flags, &TypeToRegisterSet, &RegisterAliases,
void TargetX86Base<TraitsType>::staticInit(GlobalContext *Ctx) {
Traits::initRegisterSet(Ctx->getFlags(), &TypeToRegisterSet, &RegisterAliases,
&ScratchRegs);
filterTypeToRegisterSet(Ctx, Traits::RegisterSet::Reg_NUM,
TypeToRegisterSet.data(), TypeToRegisterSet.size(),
Traits::getRegName);
PcRelFixup = Traits::FK_PcRel;
AbsFixup = Flags.getUseNonsfi() ? Traits::FK_Gotoff : Traits::FK_Abs;
AbsFixup =
Ctx->getFlags().getUseNonsfi() ? Traits::FK_Gotoff : Traits::FK_Abs;
}
template <typename TraitsType> void TargetX86Base<TraitsType>::translateO2() {
......
; Show that we know how to translate vadd.
; NOTE: We use -O2 to get rid of memory stores.
; NOTE: Restricts S and D registers to ones that will better test S/D
; register encodings.
; REQUIRES: allow_dump
; Compile using standalone assembler.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -O2 \
; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -Om1 \
; RUN: -reg-use s20,s22,d20,d22 \
; RUN: | FileCheck %s --check-prefix=ASM
; Show bytes in assembled standalone code.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -O2 | FileCheck %s --check-prefix=DIS
; RUN: --args -Om1 \
; RUN: -reg-use s20,s22,d20,d22 \
; RUN: | FileCheck %s --check-prefix=DIS
; Compile using integrated assembler.
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --args -O2 \
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --args -Om1 \
; RUN: -reg-use s20,s22,d20,d22 \
; RUN: | FileCheck %s --check-prefix=IASM
; Show bytes in assembled integrated code.
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -O2 | FileCheck %s --check-prefix=DIS
; RUN: --args -Om1 \
; RUN: -reg-use s20,s22,d20,d22 \
; RUN: | FileCheck %s --check-prefix=DIS
define internal float @testVaddFloat(float %v1, float %v2) {
; ASM-LABEL: testVaddFloat:
......@@ -26,38 +33,26 @@ define internal float @testVaddFloat(float %v1, float %v2) {
; IASM-LABEL: testVaddFloat:
entry:
; ASM-NEXT: .LtestVaddFloat$entry:
; IASM-NEXT: .LtestVaddFloat$entry:
%res = fadd float %v1, %v2
; ASM-NEXT: vadd.f32 s0, s0, s1
; DIS-NEXT: 0: ee300a20
; IASM-NEXT: .byte 0x20
; IASM-NEXT: .byte 0xa
; IASM-NEXT: .byte 0x30
; IASM-NEXT: .byte 0xee
; ASM: vadd.f32 s20, s20, s22
; DIS: 1c: ee3aaa0b
; IASM-NOT: vadd
ret float %res
}
define internal double @testVaddDouble(double %v1, double %v2) {
; ASM-LABEL: testVaddDouble:
; DIS-LABEL: 00000010 <testVaddDouble>:
; IASM-LABEL: testVaddDouble:
; DIS-LABEL: 00000040 <testVaddDouble>:
; IASM-LABEL: .LtestVaddDouble$entry:
entry:
; ASM-NEXT: .LtestVaddDouble$entry:
; IASM-NEXT: .LtestVaddDouble$entry:
%res = fadd double %v1, %v2
; ASM-NEXT: vadd.f64 d0, d0, d1
; DIS-NEXT: 10: ee300b01
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0xb
; IASM-NEXT: .byte 0x30
; IASM-NEXT: .byte 0xee
; ASM: vadd.f64 d22, d22, d20
; DIS: 54: ee766ba4
; IASM-NOT: vadd
ret double %res
}
; Show that we complain if an unknown register is specified on the command line.
; Compile using standalone assembler.
; RUN: %p2i --expect-fail --filetype=asm -i %s --target=arm32 --args -Om1 \
; RUN: -reg-use r2,xx9x,r5,yy28 -reg-exclude s1,sq5 2>&1 \
; RUN: | FileCheck %s
define void @foo() {
ret void
}
; CHECK: LLVM ERROR: Unrecognized use/exclude registers: xx9x yy28 sq5
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