summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/arm_neon.td11
-rw-r--r--test/CodeGen/arm64-lanes.c11
-rw-r--r--utils/TableGen/NeonEmitter.cpp234
3 files changed, 192 insertions, 64 deletions
diff --git a/include/clang/Basic/arm_neon.td b/include/clang/Basic/arm_neon.td
index 4dba0f1058..f68ccea655 100644
--- a/include/clang/Basic/arm_neon.td
+++ b/include/clang/Basic/arm_neon.td
@@ -261,6 +261,7 @@ class Inst <string n, string p, string t, Operation o> {
Operation Operation = o;
bit CartesianProductOfTypes = 0;
+ bit BigEndianSafe = 0;
bit isShift = 0;
bit isScalarShift = 0;
bit isScalarNarrowShift = 0;
@@ -654,7 +655,9 @@ def VSET_LANE : IInst<"vset_lane", "dsdi",
////////////////////////////////////////////////////////////////////////////////
// E.3.18 Initialize a vector from bit pattern
-def VCREATE : NoTestOpInst<"vcreate", "dl", "csihfUcUsUiUlPcPsl", OP_CAST>;
+def VCREATE : NoTestOpInst<"vcreate", "dl", "csihfUcUsUiUlPcPsl", OP_CAST> {
+ let BigEndianSafe = 1;
+}
////////////////////////////////////////////////////////////////////////////////
// E.3.19 Set all lanes to same value
@@ -791,6 +794,7 @@ def VREINTERPRET
"csilUcUsUiUlhfPcPsQcQsQiQlQUcQUsQUiQUlQhQfQPcQPs", OP_REINT> {
let CartesianProductOfTypes = 1;
let ArchGuard = "!defined(__aarch64__)";
+ let BigEndianSafe = 1;
}
////////////////////////////////////////////////////////////////////////////////
@@ -1092,7 +1096,9 @@ def COMBINE : NoTestOpInst<"vcombine", "kdd", "dPl", OP_CONC>;
////////////////////////////////////////////////////////////////////////////////
//Initialize a vector from bit pattern
-def CREATE : NoTestOpInst<"vcreate", "dl", "dPl", OP_CAST>;
+def CREATE : NoTestOpInst<"vcreate", "dl", "dPl", OP_CAST> {
+ let BigEndianSafe = 1;
+}
////////////////////////////////////////////////////////////////////////////////
@@ -1256,6 +1262,7 @@ def VVREINTERPRET
: NoTestOpInst<"vreinterpret", "dd",
"csilUcUsUiUlhfdPcPsPlQcQsQiQlQUcQUsQUiQUlQhQfQdQPcQPsQPlQPk", OP_REINT> {
let CartesianProductOfTypes = 1;
+ let BigEndianSafe = 1;
let ArchGuard = "__ARM_ARCH >= 8 && defined(__aarch64__)";
}
diff --git a/test/CodeGen/arm64-lanes.c b/test/CodeGen/arm64-lanes.c
index b0d4694677..8ab2bd4c66 100644
--- a/test/CodeGen/arm64-lanes.c
+++ b/test/CodeGen/arm64-lanes.c
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -O3 -triple arm64-apple-ios7 -target-feature +neon -ffreestanding -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -O3 -triple arm64_be-linux-gnu -target-feature +neon -ffreestanding -emit-llvm -o - %s | FileCheck %s --check-prefix CHECK-BE
#include <arm_neon.h>
@@ -6,58 +7,68 @@
int8_t test_vdupb_lane_s8(int8x8_t src) {
return vdupb_lane_s8(src, 2);
// CHECK: extractelement <8 x i8> %src, i32 2
+ // CHECK-BE: extractelement <8 x i8> %src, i32 5
}
// CHECK-LABEL: @test_vdupb_lane_u8
uint8_t test_vdupb_lane_u8(uint8x8_t src) {
return vdupb_lane_u8(src, 2);
// CHECK: extractelement <8 x i8> %src, i32 2
+ // CHECK-BE: extractelement <8 x i8> %src, i32 5
}
// CHECK-LABEL: @test_vduph_lane_s16
int16_t test_vduph_lane_s16(int16x4_t src) {
return vduph_lane_s16(src, 2);
// CHECK: extractelement <4 x i16> %src, i32 2
+ // CHECK-BE: extractelement <4 x i16> %src, i32 1
}
// CHECK-LABEL: @test_vduph_lane_u16
uint16_t test_vduph_lane_u16(uint16x4_t src) {
return vduph_lane_u16(src, 2);
// CHECK: extractelement <4 x i16> %src, i32 2
+ // CHECK-BE: extractelement <4 x i16> %src, i32 1
}
// CHECK-LABEL: @test_vdups_lane_s32
int32_t test_vdups_lane_s32(int32x2_t src) {
return vdups_lane_s32(src, 0);
// CHECK: extractelement <2 x i32> %src, i32 0
+ // CHECK-BE: extractelement <2 x i32> %src, i32 1
}
// CHECK-LABEL: @test_vdups_lane_u32
uint32_t test_vdups_lane_u32(uint32x2_t src) {
return vdups_lane_u32(src, 0);
// CHECK: extractelement <2 x i32> %src, i32 0
+ // CHECK-BE: extractelement <2 x i32> %src, i32 1
}
// CHECK-LABEL: @test_vdups_lane_f32
float32_t test_vdups_lane_f32(float32x2_t src) {
return vdups_lane_f32(src, 0);
// CHECK: extractelement <2 x float> %src, i32 0
+ // CHECK-BE: extractelement <2 x float> %src, i32 1
}
// CHECK-LABEL: @test_vdupd_lane_s64
int64_t test_vdupd_lane_s64(int64x1_t src) {
return vdupd_lane_s64(src, 0);
// CHECK: extractelement <1 x i64> %src, i32 0
+ // CHECK-BE: extractelement <1 x i64> %src, i32 0
}
// CHECK-LABEL: @test_vdupd_lane_u64
uint64_t test_vdupd_lane_u64(uint64x1_t src) {
return vdupd_lane_u64(src, 0);
// CHECK: extractelement <1 x i64> %src, i32 0
+ // CHECK-BE: extractelement <1 x i64> %src, i32 0
}
// CHECK-LABEL: @test_vdupd_lane_f64
float64_t test_vdupd_lane_f64(float64x1_t src) {
return vdupd_lane_f64(src, 0);
// CHECK: extractelement <1 x double> %src, i32 0
+ // CHECK-BE: extractelement <1 x double> %src, i32 0
}
diff --git a/utils/TableGen/NeonEmitter.cpp b/utils/TableGen/NeonEmitter.cpp
index 9d6ae3497b..d7e418a810 100644
--- a/utils/TableGen/NeonEmitter.cpp
+++ b/utils/TableGen/NeonEmitter.cpp
@@ -259,6 +259,8 @@ public:
/// The main grunt class. This represents an instantiation of an intrinsic with
/// a particular typespec and prototype.
class Intrinsic {
+ friend class DagEmitter;
+
/// The Record this intrinsic was created from.
Record *R;
/// The unmangled name and prototype.
@@ -279,6 +281,9 @@ class Intrinsic {
/// Set if the Unvailable bit is 1. This means we don't generate a body,
/// just an "unavailable" attribute on a declaration.
bool IsUnavailable;
+ /// Is this intrinsic safe for big-endian? or does it need its arguments
+ /// reversing?
+ bool BigEndianSafe;
/// The types of return value [0] and parameters [1..].
std::vector<Type> Types;
@@ -305,11 +310,11 @@ class Intrinsic {
public:
Intrinsic(Record *R, StringRef Name, StringRef Proto, TypeSpec OutTS,
TypeSpec InTS, ClassKind CK, ListInit *Body, NeonEmitter &Emitter,
- StringRef Guard, bool IsUnavailable)
+ StringRef Guard, bool IsUnavailable, bool BigEndianSafe)
: R(R), Name(Name.str()), Proto(Proto.str()), OutTS(OutTS), InTS(InTS),
CK(CK), Body(Body), Guard(Guard.str()), IsUnavailable(IsUnavailable),
- NeededEarly(false), UseMacro(false), BaseType(OutTS, 'd'),
- InBaseType(InTS, 'd'), Emitter(Emitter) {
+ BigEndianSafe(BigEndianSafe), NeededEarly(false), UseMacro(false),
+ BaseType(OutTS, 'd'), InBaseType(InTS, 'd'), Emitter(Emitter) {
// If this builtin takes an immediate argument, we need to #define it rather
// than use a standard declaration, so that SemaChecking can range check
// the immediate passed by the user.
@@ -435,25 +440,41 @@ private:
std::string replaceParamsIn(std::string S);
void emitBodyAsBuiltinCall();
- std::pair<Type, std::string> emitDagArg(Init *Arg, std::string ArgName);
- std::pair<Type, std::string> emitDagSaveTemp(DagInit *DI);
- std::pair<Type, std::string> emitDagSplat(DagInit *DI);
- std::pair<Type, std::string> emitDagDup(DagInit *DI);
- std::pair<Type, std::string> emitDagShuffle(DagInit *DI);
- std::pair<Type, std::string> emitDagCast(DagInit *DI, bool IsBitCast);
- std::pair<Type, std::string> emitDagCall(DagInit *DI);
- std::pair<Type, std::string> emitDagNameReplace(DagInit *DI);
- std::pair<Type, std::string> emitDagLiteral(DagInit *DI);
- std::pair<Type, std::string> emitDagOp(DagInit *DI);
- std::pair<Type, std::string> emitDag(DagInit *DI);
+ void generateImpl(bool ReverseArguments,
+ StringRef NamePrefix, StringRef CallPrefix);
void emitReturn();
- void emitBody();
+ void emitBody(StringRef CallPrefix);
void emitShadowedArgs();
+ void emitArgumentReversal();
+ void emitReturnReversal();
+ void emitReverseVariable(Variable &Dest, Variable &Src);
void emitNewLine();
void emitClosingBrace();
void emitOpeningBrace();
- void emitPrototype();
+ void emitPrototype(StringRef NamePrefix);
+
+ class DagEmitter {
+ Intrinsic &Intr;
+ StringRef CallPrefix;
+
+ public:
+ DagEmitter(Intrinsic &Intr, StringRef CallPrefix) :
+ Intr(Intr), CallPrefix(CallPrefix) {
+ }
+ std::pair<Type, std::string> emitDagArg(Init *Arg, std::string ArgName);
+ std::pair<Type, std::string> emitDagSaveTemp(DagInit *DI);
+ std::pair<Type, std::string> emitDagSplat(DagInit *DI);
+ std::pair<Type, std::string> emitDagDup(DagInit *DI);
+ std::pair<Type, std::string> emitDagShuffle(DagInit *DI);
+ std::pair<Type, std::string> emitDagCast(DagInit *DI, bool IsBitCast);
+ std::pair<Type, std::string> emitDagCall(DagInit *DI);
+ std::pair<Type, std::string> emitDagNameReplace(DagInit *DI);
+ std::pair<Type, std::string> emitDagLiteral(DagInit *DI);
+ std::pair<Type, std::string> emitDagOp(DagInit *DI);
+ std::pair<Type, std::string> emitDag(DagInit *DI);
+ };
+
};
//===----------------------------------------------------------------------===//
@@ -1103,13 +1124,13 @@ void Intrinsic::initVariables() {
RetVar = Variable(Types[0], "ret" + VariablePostfix);
}
-void Intrinsic::emitPrototype() {
+void Intrinsic::emitPrototype(StringRef NamePrefix) {
if (UseMacro)
OS << "#define ";
else
OS << "__ai " << Types[0].str() << " ";
- OS << mangleName(Name, ClassS) << "(";
+ OS << NamePrefix.str() << mangleName(Name, ClassS) << "(";
for (unsigned I = 0; I < getNumParams(); ++I) {
if (I != 0)
@@ -1151,6 +1172,61 @@ void Intrinsic::emitNewLine() {
OS << "\n";
}
+void Intrinsic::emitReverseVariable(Variable &Dest, Variable &Src) {
+ if (Dest.getType().getNumVectors() > 1) {
+ emitNewLine();
+
+ for (unsigned K = 0; K < Dest.getType().getNumVectors(); ++K) {
+ OS << " " << Dest.getName() << ".val[" << utostr(K) << "] = "
+ << "__builtin_shufflevector("
+ << Src.getName() << ".val[" << utostr(K) << "], "
+ << Src.getName() << ".val[" << utostr(K) << "]";
+ for (int J = Dest.getType().getNumElements() - 1; J >= 0; --J)
+ OS << ", " << utostr(J);
+ OS << ");";
+ emitNewLine();
+ }
+ } else {
+ OS << " " << Dest.getName()
+ << " = __builtin_shufflevector(" << Src.getName() << ", " << Src.getName();
+ for (int J = Dest.getType().getNumElements() - 1; J >= 0; --J)
+ OS << ", " << utostr(J);
+ OS << ");";
+ emitNewLine();
+ }
+}
+
+void Intrinsic::emitArgumentReversal() {
+ if (BigEndianSafe)
+ return;
+
+ // Reverse all vector arguments.
+ for (unsigned I = 0; I < getNumParams(); ++I) {
+ std::string Name = "p" + utostr(I);
+ std::string NewName = "rev" + utostr(I);
+
+ Variable &V = Variables[Name];
+ Variable NewV(V.getType(), NewName + VariablePostfix);
+
+ if (!NewV.getType().isVector() || NewV.getType().getNumElements() == 1)
+ continue;
+
+ OS << " " << NewV.getType().str() << " " << NewV.getName() << ";";
+ emitReverseVariable(NewV, V);
+ V = NewV;
+ }
+}
+
+void Intrinsic::emitReturnReversal() {
+ if (BigEndianSafe)
+ return;
+ if (!getReturnType().isVector() || getReturnType().isVoid() ||
+ getReturnType().getNumElements() == 1)
+ return;
+ emitReverseVariable(RetVar, RetVar);
+}
+
+
void Intrinsic::emitShadowedArgs() {
// Macro arguments are not type-checked like inline function arguments,
// so assign them to local temporaries to get the right type checking.
@@ -1167,9 +1243,7 @@ void Intrinsic::emitShadowedArgs() {
if (getParamType(I).isPointer())
continue;
- char NameC = '0' + I;
- std::string Name = "p";
- Name.push_back(NameC);
+ std::string Name = "p" + utostr(I);
assert(Variables.find(Name) != Variables.end());
Variable &V = Variables[Name];
@@ -1293,7 +1367,7 @@ void Intrinsic::emitBodyAsBuiltinCall() {
emitNewLine();
}
-void Intrinsic::emitBody() {
+void Intrinsic::emitBody(StringRef CallPrefix) {
std::vector<std::string> Lines;
assert(RetVar.getType() == Types[0]);
@@ -1314,7 +1388,8 @@ void Intrinsic::emitBody() {
if (StringInit *SI = dyn_cast<StringInit>(I)) {
Lines.push_back(replaceParamsIn(SI->getAsString()));
} else if (DagInit *DI = dyn_cast<DagInit>(I)) {
- Lines.push_back(emitDag(DI).second + ";");
+ DagEmitter DE(*this, CallPrefix);
+ Lines.push_back(DE.emitDag(DI).second + ";");
}
}
@@ -1338,7 +1413,7 @@ void Intrinsic::emitReturn() {
emitNewLine();
}
-std::pair<Type, std::string> Intrinsic::emitDag(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDag(DagInit *DI) {
// At this point we should only be seeing a def.
DefInit *DefI = cast<DefInit>(DI->getOperator());
std::string Op = DefI->getAsString();
@@ -1365,7 +1440,7 @@ std::pair<Type, std::string> Intrinsic::emitDag(DagInit *DI) {
return std::make_pair(Type::getVoid(), "");
}
-std::pair<Type, std::string> Intrinsic::emitDagOp(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagOp(DagInit *DI) {
std::string Op = cast<StringInit>(DI->getArg(0))->getAsUnquotedString();
if (DI->getNumArgs() == 2) {
// Unary op.
@@ -1383,7 +1458,7 @@ std::pair<Type, std::string> Intrinsic::emitDagOp(DagInit *DI) {
}
}
-std::pair<Type, std::string> Intrinsic::emitDagCall(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCall(DagInit *DI) {
std::vector<Type> Types;
std::vector<std::string> Values;
for (unsigned I = 0; I < DI->getNumArgs() - 1; ++I) {
@@ -1399,15 +1474,15 @@ std::pair<Type, std::string> Intrinsic::emitDagCall(DagInit *DI) {
N = SI->getAsUnquotedString();
else
N = emitDagArg(DI->getArg(0), "").second;
- Intrinsic *Callee = Emitter.getIntrinsic(N, Types);
+ Intrinsic *Callee = Intr.Emitter.getIntrinsic(N, Types);
assert(Callee && "getIntrinsic should not return us nullptr!");
// Make sure the callee is known as an early def.
Callee->setNeededEarly();
- Dependencies.insert(Callee);
+ Intr.Dependencies.insert(Callee);
// Now create the call itself.
- std::string S = Callee->getMangledName(true) + "(";
+ std::string S = CallPrefix.str() + Callee->getMangledName(true) + "(";
for (unsigned I = 0; I < DI->getNumArgs() - 1; ++I) {
if (I != 0)
S += ", ";
@@ -1418,8 +1493,8 @@ std::pair<Type, std::string> Intrinsic::emitDagCall(DagInit *DI) {
return std::make_pair(Callee->getReturnType(), S);
}
-std::pair<Type, std::string> Intrinsic::emitDagCast(DagInit *DI,
- bool IsBitCast) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCast(DagInit *DI,
+ bool IsBitCast){
// (cast MOD* VAL) -> cast VAL to type given by MOD.
std::pair<Type, std::string> R = emitDagArg(
DI->getArg(DI->getNumArgs() - 1), DI->getArgName(DI->getNumArgs() - 1));
@@ -1434,15 +1509,16 @@ std::pair<Type, std::string> Intrinsic::emitDagCast(DagInit *DI,
// 5. The value "H" or "D" to half or double the bitwidth.
// 6. The value "8" to convert to 8-bit (signed) integer lanes.
if (DI->getArgName(ArgIdx).size()) {
- assert_with_loc(Variables.find(DI->getArgName(ArgIdx)) != Variables.end(),
+ assert_with_loc(Intr.Variables.find(DI->getArgName(ArgIdx)) !=
+ Intr.Variables.end(),
"Variable not found");
- castToType = Variables[DI->getArgName(ArgIdx)].getType();
+ castToType = Intr.Variables[DI->getArgName(ArgIdx)].getType();
} else {
StringInit *SI = dyn_cast<StringInit>(DI->getArg(ArgIdx));
assert_with_loc(SI, "Expected string type or $Name for cast type");
if (SI->getAsUnquotedString() == "R") {
- castToType = getReturnType();
+ castToType = Intr.getReturnType();
} else if (SI->getAsUnquotedString() == "U") {
castToType.makeUnsigned();
} else if (SI->getAsUnquotedString() == "S") {
@@ -1466,15 +1542,15 @@ std::pair<Type, std::string> Intrinsic::emitDagCast(DagInit *DI,
// a temporary.
std::string N = "reint";
unsigned I = 0;
- while (Variables.find(N) != Variables.end())
+ while (Intr.Variables.find(N) != Intr.Variables.end())
N = "reint" + utostr(++I);
- Variables[N] = Variable(R.first, N + VariablePostfix);
+ Intr.Variables[N] = Variable(R.first, N + Intr.VariablePostfix);
- OS << R.first.str() << " " << Variables[N].getName() << " = " << R.second
- << ";";
- emitNewLine();
+ Intr.OS << R.first.str() << " " << Intr.Variables[N].getName() << " = "
+ << R.second << ";";
+ Intr.emitNewLine();
- S = "*(" + castToType.str() + " *) &" + Variables[N].getName() + "";
+ S = "*(" + castToType.str() + " *) &" + Intr.Variables[N].getName() + "";
} else {
// Emit a normal (static) cast.
S = "(" + castToType.str() + ")(" + R.second + ")";
@@ -1483,7 +1559,7 @@ std::pair<Type, std::string> Intrinsic::emitDagCast(DagInit *DI,
return std::make_pair(castToType, S);
}
-std::pair<Type, std::string> Intrinsic::emitDagShuffle(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagShuffle(DagInit *DI){
// See the documentation in arm_neon.td for a description of these operators.
class LowHalf : public SetTheory::Operator {
public:
@@ -1598,12 +1674,12 @@ std::pair<Type, std::string> Intrinsic::emitDagShuffle(DagInit *DI) {
return std::make_pair(T, S);
}
-std::pair<Type, std::string> Intrinsic::emitDagDup(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagDup(DagInit *DI) {
assert_with_loc(DI->getNumArgs() == 1, "dup() expects one argument");
std::pair<Type, std::string> A = emitDagArg(DI->getArg(0), DI->getArgName(0));
assert_with_loc(A.first.isScalar(), "dup() expects a scalar argument");
- Type T = getBaseType();
+ Type T = Intr.getBaseType();
assert_with_loc(T.isVector(), "dup() used but default type is scalar!");
std::string S = "(" + T.str() + ") {";
for (unsigned I = 0; I < T.getNumElements(); ++I) {
@@ -1616,7 +1692,7 @@ std::pair<Type, std::string> Intrinsic::emitDagDup(DagInit *DI) {
return std::make_pair(T, S);
}
-std::pair<Type, std::string> Intrinsic::emitDagSplat(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagSplat(DagInit *DI) {
assert_with_loc(DI->getNumArgs() == 2, "splat() expects two arguments");
std::pair<Type, std::string> A = emitDagArg(DI->getArg(0), DI->getArgName(0));
std::pair<Type, std::string> B = emitDagArg(DI->getArg(1), DI->getArgName(1));
@@ -1625,15 +1701,15 @@ std::pair<Type, std::string> Intrinsic::emitDagSplat(DagInit *DI) {
"splat() requires a scalar int as the second argument");
std::string S = "__builtin_shufflevector(" + A.second + ", " + A.second;
- for (unsigned I = 0; I < BaseType.getNumElements(); ++I) {
+ for (unsigned I = 0; I < Intr.getBaseType().getNumElements(); ++I) {
S += ", " + B.second;
}
S += ")";
- return std::make_pair(BaseType, S);
+ return std::make_pair(Intr.getBaseType(), S);
}
-std::pair<Type, std::string> Intrinsic::emitDagSaveTemp(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagSaveTemp(DagInit *DI) {
assert_with_loc(DI->getNumArgs() == 2, "save_temp() expects two arguments");
std::pair<Type, std::string> A = emitDagArg(DI->getArg(1), DI->getArgName(1));
@@ -1643,18 +1719,19 @@ std::pair<Type, std::string> Intrinsic::emitDagSaveTemp(DagInit *DI) {
std::string N = DI->getArgName(0);
assert_with_loc(N.size(), "save_temp() expects a name as the first argument");
- assert_with_loc(Variables.find(N) == Variables.end(),
+ assert_with_loc(Intr.Variables.find(N) == Intr.Variables.end(),
"Variable already defined!");
- Variables[N] = Variable(A.first, N + VariablePostfix);
+ Intr.Variables[N] = Variable(A.first, N + Intr.VariablePostfix);
std::string S =
- A.first.str() + " " + Variables[N].getName() + " = " + A.second;
+ A.first.str() + " " + Intr.Variables[N].getName() + " = " + A.second;
return std::make_pair(Type::getVoid(), S);
}
-std::pair<Type, std::string> Intrinsic::emitDagNameReplace(DagInit *DI) {
- std::string S = Name;
+std::pair<Type, std::string>
+Intrinsic::DagEmitter::emitDagNameReplace(DagInit *DI) {
+ std::string S = Intr.Name;
assert_with_loc(DI->getNumArgs() == 2, "name_replace requires 2 arguments!");
std::string ToReplace = cast<StringInit>(DI->getArg(0))->getAsUnquotedString();
@@ -1668,20 +1745,20 @@ std::pair<Type, std::string> Intrinsic::emitDagNameReplace(DagInit *DI) {
return std::make_pair(Type::getVoid(), S);
}
-std::pair<Type, std::string> Intrinsic::emitDagLiteral(DagInit *DI) {
+std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagLiteral(DagInit *DI){
std::string Ty = cast<StringInit>(DI->getArg(0))->getAsUnquotedString();
std::string Value = cast<StringInit>(DI->getArg(1))->getAsUnquotedString();
return std::make_pair(Type::fromTypedefName(Ty), Value);
}
-std::pair<Type, std::string> Intrinsic::emitDagArg(Init *Arg,
- std::string ArgName) {
+std::pair<Type, std::string>
+Intrinsic::DagEmitter::emitDagArg(Init *Arg, std::string ArgName) {
if (ArgName.size()) {
assert_with_loc(!Arg->isComplete(),
"Arguments must either be DAGs or names, not both!");
- assert_with_loc(Variables.find(ArgName) != Variables.end(),
+ assert_with_loc(Intr.Variables.find(ArgName) != Intr.Variables.end(),
"Variable not defined!");
- Variable &V = Variables[ArgName];
+ Variable &V = Intr.Variables[ArgName];
return std::make_pair(V.getType(), V.getName());
}
@@ -1693,6 +1770,35 @@ std::pair<Type, std::string> Intrinsic::emitDagArg(Init *Arg,
}
std::string Intrinsic::generate() {
+ // Little endian intrinsics are simple and don't require any argument
+ // swapping.
+ OS << "#ifdef __LITTLE_ENDIAN__\n";
+
+ generateImpl(false, "", "");
+
+ OS << "#else\n";
+
+ // Big endian intrinsics are more complex. The user intended these
+ // intrinsics to operate on a vector "as-if" loaded by (V)LDR,
+ // but we load as-if (V)LD1. So we should swap all arguments and
+ // swap the return value too.
+ //
+ // If we call sub-intrinsics, we should call a version that does
+ // not re-swap the arguments!
+ generateImpl(true, "", "__noswap_");
+
+ // If we're needed early, create a non-swapping variant for
+ // big-endian.
+ if (NeededEarly) {
+ generateImpl(false, "__noswap_", "__noswap_");
+ }
+ OS << "#endif\n\n";
+
+ return OS.str();
+}
+
+void Intrinsic::generateImpl(bool ReverseArguments,
+ StringRef NamePrefix, StringRef CallPrefix) {
CurrentRecord = R;
// If we call a macro, our local variables may be corrupted due to
@@ -1708,28 +1814,31 @@ std::string Intrinsic::generate() {
initVariables();
- emitPrototype();
+ emitPrototype(NamePrefix);
if (IsUnavailable) {
OS << " __attribute__((unavailable));";
} else {
emitOpeningBrace();
emitShadowedArgs();
- emitBody();
+ if (ReverseArguments)
+ emitArgumentReversal();
+ emitBody(CallPrefix);
+ if (ReverseArguments)
+ emitReturnReversal();
emitReturn();
emitClosingBrace();
}
OS << "\n";
CurrentRecord = nullptr;
- return OS.str();
}
void Intrinsic::indexBody() {
CurrentRecord = R;
initVariables();
- emitBody();
+ emitBody("");
OS.str("");
CurrentRecord = nullptr;
@@ -1796,6 +1905,7 @@ void NeonEmitter::createIntrinsic(Record *R,
std::string Types = R->getValueAsString("Types");
Record *OperationRec = R->getValueAsDef("Operation");
bool CartesianProductOfTypes = R->getValueAsBit("CartesianProductOfTypes");
+ bool BigEndianSafe = R->getValueAsBit("BigEndianSafe");
std::string Guard = R->getValueAsString("ArchGuard");
bool IsUnavailable = OperationRec->getValueAsBit("Unavailable");
@@ -1832,7 +1942,7 @@ void NeonEmitter::createIntrinsic(Record *R,
for (auto &I : NewTypeSpecs) {
Intrinsic *IT = new Intrinsic(R, Name, Proto, I.first, I.second, CK, Body,
- *this, Guard, IsUnavailable);
+ *this, Guard, IsUnavailable, BigEndianSafe);
IntrinsicMap[Name].push_back(IT);
Out.push_back(IT);