Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (194038 => 194039)
--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2015-12-14 18:07:19 UTC (rev 194038)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2015-12-14 19:13:31 UTC (rev 194039)
@@ -873,20 +873,144 @@
const CompareFloatFunctor& compareFloat, // Signature: (Arg doubleCond, Arg, Arg) -> Inst
bool inverted = false)
{
- // Chew through any negations. It's not strictly necessary for this to be a loop, but we like
- // to follow the rule that the instruction selector reduces strength whenever it doesn't
- // require making things more complicated.
+ // NOTE: This is totally happy to match comparisons that have already been computed elsewhere
+ // since on most architectures, the cost of branching on a previously computed comparison
+ // result is almost always higher than just doing another fused compare/branch. The only time
+ // it could be worse is if we have a binary comparison and both operands are variables (not
+ // constants), and we encounter register pressure. Even in this case, duplicating the compare
+ // so that we can fuse it to the branch will be more efficient most of the time, since
+ // register pressure is not *that* common. For this reason, this algorithm will always
+ // duplicate the comparison.
+ //
+ // However, we cannot duplicate loads. The canBeInternal() on a load will assume that we
+ // already validated canBeInternal() on all of the values that got us to the load. So, even
+ // if we are sharing a value, we still need to call canBeInternal() for the purpose of
+ // tracking whether we are still in good shape to fuse loads.
+ //
+ // We could even have a chain of compare values that we fuse, and any member of the chain
+ // could be shared. Once any of them are shared, then the shared one's transitive children
+ // cannot be locked (i.e. commitInternal()). But if none of them are shared, then we want to
+ // lock all of them because that's a prerequisite to fusing the loads so that the loads don't
+ // get duplicated. For example, we might have:
+ //
+ // @tmp1 = LessThan(@a, @b)
+ // @tmp2 = Equal(@tmp1, 0)
+ // Branch(@tmp2)
+ //
+ // If either @a or @b are loads, then we want to have locked @tmp1 and @tmp2 so that they
+ // don't emit the loads a second time. But if we had another use of @tmp2, then we cannot
+ // lock @tmp1 (or @a or @b) because then we'll get into trouble when the other values that
+ // try to share @tmp1 with us try to do their lowering.
+ //
+ // There's one more wrinkle. If we don't lock an internal value, then this internal value may
+ // have already separately locked its children. So, if we're not locking a value then we need
+ // to make sure that its children aren't locked. We encapsulate this in two ways:
+ //
+ // canCommitInternal: This variable tells us if the values that we've fused so far are
+ // locked. This means that we're not sharing any of them with anyone. This permits us to fuse
+ // loads. If it's false, then we cannot fuse loads and we also need to ensure that the
+ // children of any values we try to fuse-by-sharing are not already locked. You don't have to
+ // worry about the children locking thing if you use prepareToFuse() before trying to fuse a
+ // sharable value. But, you do need to guard any load fusion by checking if canCommitInternal
+ // is true.
+ //
+ // FusionResult prepareToFuse(value): Call this when you think that you would like to fuse
+ // some value and that value is not a load. It will automatically handle the shared-or-locked
+ // issues and it will clear canCommitInternal if necessary. This will return CannotFuse
+ // (which acts like false) if the value cannot be locked and its children are locked. That's
+ // rare, but you just need to make sure that you do smart things when this happens (i.e. just
+ // use the value rather than trying to fuse it). After you call prepareToFuse(), you can
+ // still change your mind about whether you will actually fuse the value. If you do fuse it,
+ // you need to call commitFusion(value, fusionResult).
+ //
+ // commitFusion(value, fusionResult): Handles calling commitInternal(value) if fusionResult
+ // is FuseAndCommit.
+
+ bool canCommitInternal = true;
+
+ enum FusionResult {
+ CannotFuse,
+ FuseAndCommit,
+ Fuse
+ };
+ auto prepareToFuse = [&] (Value* value) -> FusionResult {
+ if (value == m_value) {
+ // It's not actually internal. It's the root value. We're good to go.
+ return Fuse;
+ }
+
+ if (canCommitInternal && canBeInternal(value)) {
+ // We are the only users of this value. This also means that the value's children
+ // could not have been locked, since we have now proved that m_value dominates value
+ // in the data flow graph. To only other way to value is from a user of m_value. If
+ // value's children are shared with others, then they could not have been locked
+ // because their use count is greater than 1. If they are only used from value, then
+ // in order for value's children to be locked, value would also have to be locked,
+ // and we just proved that it wasn't.
+ return FuseAndCommit;
+ }
+
+ // We're going to try to share value with others. It's possible that some other basic
+ // block had already emitted code for value and then matched over its children and then
+ // locked them, in which case we just want to use value instead of duplicating it. So, we
+ // validate the children. Note that this only arises in linear chains like:
+ //
+ // BB#1:
+ // @1 = Foo(...)
+ // @2 = Bar(@1)
+ // Jump(#2)
+ // BB#2:
+ // @3 = Baz(@2)
+ //
+ // Notice how we could start by generating code for BB#1 and then decide to lock @1 when
+ // generating code for @2, if we have some way of fusing Bar and Foo into a single
+ // instruction. This is legal, since indeed @1 only has one user. The fact that @2 now
+ // has a tmp (i.e. @2 is pinned), canBeInternal(@2) will return false, which brings us
+ // here. In that case, we cannot match over @2 because then we'd hit a hazard if we end
+ // up deciding not to fuse Foo into the fused Baz/Bar.
+ //
+ // Happily, there are only two places where this kind of child validation happens is in
+ // rules that admit sharing, like this and effectiveAddress().
+ //
+ // N.B. We could probably avoid the need to do value locking if we committed to a well
+ // chosen code generation order. For example, if we guaranteed that all of the users of
+ // a value get generated before that value, then there's no way for the lowering of @3 to
+ // see @1 locked. But we don't want to do that, since this is a greedy instruction
+ // selector and so we want to be able to play with order.
+ for (Value* child : value->children()) {
+ if (m_locked.contains(child))
+ return CannotFuse;
+ }
+
+ // It's safe to share value, but since we're sharing, it means that we aren't locking it.
+ // If we don't lock it, then fusing loads is off limits and all of value's children will
+ // have to go through the sharing path as well.
+ canCommitInternal = false;
+
+ return Fuse;
+ };
+
+ auto commitFusion = [&] (Value* value, FusionResult result) {
+ if (result == FuseAndCommit)
+ commitInternal(value);
+ };
+
+ // Chew through any inversions. This loop isn't necessary for comparisons and branches, but
+ // we do need at least one iteration of it for Check.
for (;;) {
- if (!canBeInternal(value) && value != m_value)
- break;
bool shouldInvert =
(value->opcode() == BitXor && value->child(1)->hasInt() && (value->child(1)->asInt() & 1) && value->child(0)->returnsBool())
|| (value->opcode() == Equal && value->child(1)->isInt(0));
if (!shouldInvert)
break;
+
+ FusionResult fusionResult = prepareToFuse(value);
+ if (fusionResult == CannotFuse)
+ break;
+ commitFusion(value, fusionResult);
+
value = value->child(0);
inverted = !inverted;
- commitInternal(value);
}
auto createRelCond = [&] (
@@ -931,51 +1055,55 @@
return Inst();
};
- // First handle compares that involve fewer bits than B3's type system supports.
- // This is pretty important. For example, we want this to be a single instruction:
- //
- // @1 = Load8S(...)
- // @2 = Const32(...)
- // @3 = LessThan(@1, @2)
- // Branch(@3)
+ Arg::Width width = Arg::widthForB3Type(value->child(0)->type());
- if (relCond.isSignedCond()) {
- if (Inst result = tryCompareLoadImm(Arg::Width8, Load8S, Arg::Signed))
- return result;
- }
+ if (canCommitInternal) {
+ // First handle compares that involve fewer bits than B3's type system supports.
+ // This is pretty important. For example, we want this to be a single
+ // instruction:
+ //
+ // @1 = Load8S(...)
+ // @2 = Const32(...)
+ // @3 = LessThan(@1, @2)
+ // Branch(@3)
- if (relCond.isUnsignedCond()) {
- if (Inst result = tryCompareLoadImm(Arg::Width8, Load8Z, Arg::Unsigned))
- return result;
- }
+ if (relCond.isSignedCond()) {
+ if (Inst result = tryCompareLoadImm(Arg::Width8, Load8S, Arg::Signed))
+ return result;
+ }
+
+ if (relCond.isUnsignedCond()) {
+ if (Inst result = tryCompareLoadImm(Arg::Width8, Load8Z, Arg::Unsigned))
+ return result;
+ }
- if (relCond.isSignedCond()) {
- if (Inst result = tryCompareLoadImm(Arg::Width16, Load16S, Arg::Signed))
- return result;
- }
+ if (relCond.isSignedCond()) {
+ if (Inst result = tryCompareLoadImm(Arg::Width16, Load16S, Arg::Signed))
+ return result;
+ }
- if (relCond.isUnsignedCond()) {
- if (Inst result = tryCompareLoadImm(Arg::Width16, Load16Z, Arg::Unsigned))
- return result;
- }
+ if (relCond.isUnsignedCond()) {
+ if (Inst result = tryCompareLoadImm(Arg::Width16, Load16Z, Arg::Unsigned))
+ return result;
+ }
- // Now handle compares that involve a load and an immediate.
+ // Now handle compares that involve a load and an immediate.
- Arg::Width width = Arg::widthForB3Type(value->child(0)->type());
- if (Inst result = tryCompareLoadImm(width, Load, Arg::Signed))
- return result;
+ if (Inst result = tryCompareLoadImm(width, Load, Arg::Signed))
+ return result;
- // Now handle compares that involve a load. It's not obvious that it's better to
- // handle this before the immediate cases or not. Probably doesn't matter.
+ // Now handle compares that involve a load. It's not obvious that it's better to
+ // handle this before the immediate cases or not. Probably doesn't matter.
- if (Inst result = tryCompare(width, loadPromise(left), tmpPromise(right))) {
- commitInternal(left);
- return result;
- }
+ if (Inst result = tryCompare(width, loadPromise(left), tmpPromise(right))) {
+ commitInternal(left);
+ return result;
+ }
- if (Inst result = tryCompare(width, tmpPromise(left), loadPromise(right))) {
- commitInternal(right);
- return result;
+ if (Inst result = tryCompare(width, tmpPromise(left), loadPromise(right))) {
+ commitInternal(right);
+ return result;
+ }
}
// Now handle compares that involve an immediate and a tmp.
@@ -1062,41 +1190,43 @@
return Inst();
};
- // First handle test's that involve fewer bits than B3's type system supports.
+ if (canCommitInternal) {
+ // First handle test's that involve fewer bits than B3's type system supports.
- if (Inst result = tryTestLoadImm(Arg::Width8, Load8Z))
- return result;
+ if (Inst result = tryTestLoadImm(Arg::Width8, Load8Z))
+ return result;
+
+ if (Inst result = tryTestLoadImm(Arg::Width8, Load8S))
+ return result;
+
+ if (Inst result = tryTestLoadImm(Arg::Width16, Load16Z))
+ return result;
+
+ if (Inst result = tryTestLoadImm(Arg::Width16, Load16S))
+ return result;
- if (Inst result = tryTestLoadImm(Arg::Width8, Load8S))
- return result;
-
- if (Inst result = tryTestLoadImm(Arg::Width16, Load16Z))
- return result;
-
- if (Inst result = tryTestLoadImm(Arg::Width16, Load16S))
- return result;
-
- // Now handle test's that involve a load and an immediate. Note that immediates are
- // 32-bit, and we want zero-extension. Hence, the immediate form is compiled as a
- // 32-bit test. Note that this spits on the grave of inferior endians, such as the
- // big one.
-
- if (Inst result = tryTestLoadImm(Arg::Width32, Load))
- return result;
-
- // Now handle test's that involve a load.
-
- Arg::Width width = Arg::widthForB3Type(value->child(0)->type());
- if (Inst result = tryTest(width, loadPromise(left), tmpPromise(right))) {
- commitInternal(left);
- return result;
+ // Now handle test's that involve a load and an immediate. Note that immediates
+ // are 32-bit, and we want zero-extension. Hence, the immediate form is compiled
+ // as a 32-bit test. Note that this spits on the grave of inferior endians, such
+ // as the big one.
+
+ if (Inst result = tryTestLoadImm(Arg::Width32, Load))
+ return result;
+
+ // Now handle test's that involve a load.
+
+ Arg::Width width = Arg::widthForB3Type(value->child(0)->type());
+ if (Inst result = tryTest(width, loadPromise(left), tmpPromise(right))) {
+ commitInternal(left);
+ return result;
+ }
+
+ if (Inst result = tryTest(width, tmpPromise(left), loadPromise(right))) {
+ commitInternal(right);
+ return result;
+ }
}
- if (Inst result = tryTest(width, tmpPromise(left), loadPromise(right))) {
- commitInternal(right);
- return result;
- }
-
// Now handle test's that involve an immediate and a tmp.
if (leftImm && leftImm.isRepresentableAs<uint32_t>()) {
@@ -1117,9 +1247,9 @@
}
};
- if (canBeInternal(value) || value == m_value) {
+ if (FusionResult fusionResult = prepareToFuse(value)) {
if (Inst result = attemptFused()) {
- commitInternal(value);
+ commitFusion(value, fusionResult);
return result;
}
}
@@ -1463,6 +1593,21 @@
}
case BitAnd: {
+ if (m_value->child(1)->isInt(0xff)) {
+ appendUnOp<ZeroExtend8To32, ZeroExtend8To32>(m_value->child(0));
+ return;
+ }
+
+ if (m_value->child(1)->isInt(0xffff)) {
+ appendUnOp<ZeroExtend16To32, ZeroExtend16To32>(m_value->child(0));
+ return;
+ }
+
+ if (m_value->child(1)->isInt(0xffffffff)) {
+ appendUnOp<Move32, Move32>(m_value->child(0));
+ return;
+ }
+
appendBinOp<And32, And64, AndDouble, AndFloat, Commutative>(
m_value->child(0), m_value->child(1));
return;
@@ -1572,18 +1717,31 @@
return;
}
+ case SExt8: {
+ appendUnOp<SignExtend8To32, Air::Oops>(m_value->child(0));
+ return;
+ }
+
+ case SExt16: {
+ appendUnOp<SignExtend16To32, Air::Oops>(m_value->child(0));
+ return;
+ }
+
case ZExt32: {
if (highBitsAreZero(m_value->child(0))) {
ASSERT(tmp(m_value->child(0)) == tmp(m_value));
return;
}
- append(Move32, tmp(m_value->child(0)), tmp(m_value));
+ appendUnOp<Move32, Air::Oops>(m_value->child(0));
return;
}
case SExt32: {
- append(SignExtend32ToPtr, tmp(m_value->child(0)), tmp(m_value));
+ // FIXME: We should have support for movsbq/movswq
+ // https://bugs.webkit.org/show_bug.cgi?id=152232
+
+ appendUnOp<SignExtend32ToPtr, Air::Oops>(m_value->child(0));
return;
}
Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (194038 => 194039)
--- trunk/Source/_javascript_Core/b3/testb3.cpp 2015-12-14 18:07:19 UTC (rev 194038)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp 2015-12-14 19:13:31 UTC (rev 194039)
@@ -71,8 +71,16 @@
namespace {
+StaticLock crashLock;
+
// Nothing fancy for now; we just use the existing WTF assertion machinery.
-#define CHECK(x) RELEASE_ASSERT(x)
+#define CHECK(x) do { \
+ if (!!(x)) \
+ break; \
+ crashLock.lock(); \
+ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #x); \
+ CRASH(); \
+ } while (false)
VM* vm;
@@ -5693,6 +5701,163 @@
CHECK(invoke<int>(*code, &value - 2, 1) == 42);
}
+void testCheckTwoMegaCombos()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index = root->appendNew<Value>(
+ proc, ZExt32, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
+
+ Value* ptr = root->appendNew<Value>(
+ proc, Add, Origin(), base,
+ root->appendNew<Value>(
+ proc, Shl, Origin(), index,
+ root->appendNew<Const32Value>(proc, Origin(), 1)));
+
+ Value* predicate = root->appendNew<Value>(
+ proc, LessThan, Origin(),
+ root->appendNew<MemoryValue>(proc, Load8S, Origin(), ptr),
+ root->appendNew<Const32Value>(proc, Origin(), 42));
+
+ CheckValue* check = root->appendNew<CheckValue>(proc, Check, Origin(), predicate);
+ check->setGenerator(
+ [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ CHECK(params.size() == 1);
+
+ // This should always work because a function this simple should never have callee
+ // saves.
+ jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+ jit.emitFunctionEpilogue();
+ jit.ret();
+ });
+ CheckValue* check2 = root->appendNew<CheckValue>(proc, Check, Origin(), predicate);
+ check2->setGenerator(
+ [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ CHECK(params.size() == 1);
+
+ // This should always work because a function this simple should never have callee
+ // saves.
+ jit.move(CCallHelpers::TrustedImm32(43), GPRInfo::returnValueGPR);
+ jit.emitFunctionEpilogue();
+ jit.ret();
+ });
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
+
+ auto code = compile(proc);
+
+ int8_t value;
+ value = 42;
+ CHECK(invoke<int>(*code, &value - 2, 1) == 0);
+ value = 127;
+ CHECK(invoke<int>(*code, &value - 2, 1) == 0);
+ value = 41;
+ CHECK(invoke<int>(*code, &value - 2, 1) == 42);
+ value = 0;
+ CHECK(invoke<int>(*code, &value - 2, 1) == 42);
+ value = -1;
+ CHECK(invoke<int>(*code, &value - 2, 1) == 42);
+}
+
+void testCheckTwoNonRedundantMegaCombos()
+{
+ Procedure proc;
+
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index = root->appendNew<Value>(
+ proc, ZExt32, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
+ Value* branchPredicate = root->appendNew<Value>(
+ proc, BitAnd, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)),
+ root->appendNew<Const32Value>(proc, Origin(), 0xff));
+
+ Value* ptr = root->appendNew<Value>(
+ proc, Add, Origin(), base,
+ root->appendNew<Value>(
+ proc, Shl, Origin(), index,
+ root->appendNew<Const32Value>(proc, Origin(), 1)));
+
+ Value* checkPredicate = root->appendNew<Value>(
+ proc, LessThan, Origin(),
+ root->appendNew<MemoryValue>(proc, Load8S, Origin(), ptr),
+ root->appendNew<Const32Value>(proc, Origin(), 42));
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(), branchPredicate,
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ CheckValue* check = thenCase->appendNew<CheckValue>(proc, Check, Origin(), checkPredicate);
+ check->setGenerator(
+ [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ CHECK(params.size() == 1);
+
+ // This should always work because a function this simple should never have callee
+ // saves.
+ jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+ jit.emitFunctionEpilogue();
+ jit.ret();
+ });
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(), thenCase->appendNew<Const32Value>(proc, Origin(), 43));
+
+ CheckValue* check2 = elseCase->appendNew<CheckValue>(proc, Check, Origin(), checkPredicate);
+ check2->setGenerator(
+ [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ CHECK(params.size() == 1);
+
+ // This should always work because a function this simple should never have callee
+ // saves.
+ jit.move(CCallHelpers::TrustedImm32(44), GPRInfo::returnValueGPR);
+ jit.emitFunctionEpilogue();
+ jit.ret();
+ });
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(), elseCase->appendNew<Const32Value>(proc, Origin(), 45));
+
+ auto code = compile(proc);
+
+ int8_t value;
+
+ value = 42;
+ CHECK(invoke<int>(*code, &value - 2, 1, true) == 43);
+ value = 127;
+ CHECK(invoke<int>(*code, &value - 2, 1, true) == 43);
+ value = 41;
+ CHECK(invoke<int>(*code, &value - 2, 1, true) == 42);
+ value = 0;
+ CHECK(invoke<int>(*code, &value - 2, 1, true) == 42);
+ value = -1;
+ CHECK(invoke<int>(*code, &value - 2, 1, true) == 42);
+
+ value = 42;
+ CHECK(invoke<int>(*code, &value - 2, 1, false) == 45);
+ value = 127;
+ CHECK(invoke<int>(*code, &value - 2, 1, false) == 45);
+ value = 41;
+ CHECK(invoke<int>(*code, &value - 2, 1, false) == 44);
+ value = 0;
+ CHECK(invoke<int>(*code, &value - 2, 1, false) == 44);
+ value = -1;
+ CHECK(invoke<int>(*code, &value - 2, 1, false) == 44);
+}
+
void testCheckAddImm()
{
Procedure proc;
@@ -7270,6 +7435,238 @@
CHECK(compileAndRun<int32_t>(proc, value) == value);
}
+void testSExt8(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int8_t>(value)));
+}
+
+void testSExt8Fold(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Const32Value>(proc, Origin(), value)));
+
+ CHECK(compileAndRun<int32_t>(proc) == static_cast<int32_t>(static_cast<int8_t>(value)));
+}
+
+void testSExt8SExt8(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int8_t>(value)));
+}
+
+void testSExt8SExt16(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int8_t>(value)));
+}
+
+void testSExt8BitAnd(int32_t value, int32_t mask)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Value>(
+ proc, BitAnd, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), mask))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int8_t>(value & mask)));
+}
+
+void testBitAndSExt8(int32_t value, int32_t mask)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, BitAnd, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+ root->appendNew<Const32Value>(proc, Origin(), mask)));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == (static_cast<int32_t>(static_cast<int8_t>(value)) & mask));
+}
+
+void testSExt16(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int16_t>(value)));
+}
+
+void testSExt16Fold(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Const32Value>(proc, Origin(), value)));
+
+ CHECK(compileAndRun<int32_t>(proc) == static_cast<int32_t>(static_cast<int16_t>(value)));
+}
+
+void testSExt16SExt16(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int16_t>(value)));
+}
+
+void testSExt16SExt8(int32_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Value>(
+ proc, SExt8, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int8_t>(value)));
+}
+
+void testSExt16BitAnd(int32_t value, int32_t mask)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Value>(
+ proc, BitAnd, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), mask))));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == static_cast<int32_t>(static_cast<int16_t>(value & mask)));
+}
+
+void testBitAndSExt16(int32_t value, int32_t mask)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, BitAnd, Origin(),
+ root->appendNew<Value>(
+ proc, SExt16, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+ root->appendNew<Const32Value>(proc, Origin(), mask)));
+
+ CHECK(compileAndRun<int32_t>(proc, value) == (static_cast<int32_t>(static_cast<int16_t>(value)) & mask));
+}
+
+void testSExt32BitAnd(int32_t value, int32_t mask)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, SExt32, Origin(),
+ root->appendNew<Value>(
+ proc, BitAnd, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), mask))));
+
+ CHECK(compileAndRun<int64_t>(proc, value) == static_cast<int64_t>(value & mask));
+}
+
+void testBitAndSExt32(int32_t value, int64_t mask)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, BitAnd, Origin(),
+ root->appendNew<Value>(
+ proc, SExt32, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+ root->appendNew<Const64Value>(proc, Origin(), mask)));
+
+ CHECK(compileAndRun<int64_t>(proc, value) == (static_cast<int64_t>(value) & mask));
+}
+
void testBasicSelect()
{
Procedure proc;
@@ -7937,6 +8334,12 @@
RUN(testBitAndArgImm(43, 0));
RUN(testBitAndArgImm(10, 3));
RUN(testBitAndArgImm(42, 0xffffffffffffffff));
+ RUN(testBitAndArgImm(42, 0xff));
+ RUN(testBitAndArgImm(300, 0xff));
+ RUN(testBitAndArgImm(-300, 0xff));
+ RUN(testBitAndArgImm(42, 0xffff));
+ RUN(testBitAndArgImm(40000, 0xffff));
+ RUN(testBitAndArgImm(-40000, 0xffff));
RUN(testBitAndImmArg(43, 43));
RUN(testBitAndImmArg(43, 0));
RUN(testBitAndImmArg(10, 3));
@@ -7967,6 +8370,12 @@
RUN(testBitAndImmArg32(43, 0));
RUN(testBitAndImmArg32(10, 3));
RUN(testBitAndImmArg32(42, 0xffffffff));
+ RUN(testBitAndImmArg32(42, 0xff));
+ RUN(testBitAndImmArg32(300, 0xff));
+ RUN(testBitAndImmArg32(-300, 0xff));
+ RUN(testBitAndImmArg32(42, 0xffff));
+ RUN(testBitAndImmArg32(40000, 0xffff));
+ RUN(testBitAndImmArg32(-40000, 0xffff));
RUN(testBitAndBitAndArgImmImm32(2, 7, 3));
RUN(testBitAndBitAndArgImmImm32(1, 6, 6));
RUN(testBitAndBitAndArgImmImm32(0xffff, 24, 7));
@@ -8346,6 +8755,8 @@
RUN(testSimpleCheck());
RUN(testCheckLessThan());
RUN(testCheckMegaCombo());
+ RUN(testCheckTwoMegaCombos());
+ RUN(testCheckTwoNonRedundantMegaCombos());
RUN(testCheckAddImm());
RUN(testCheckAddImmCommute());
RUN(testCheckAddImmSomeRegister());
@@ -8545,6 +8956,201 @@
RUN(testTruncSExt32(1000000000ll));
RUN(testTruncSExt32(-1000000000ll));
+ RUN(testSExt8(0));
+ RUN(testSExt8(1));
+ RUN(testSExt8(42));
+ RUN(testSExt8(-1));
+ RUN(testSExt8(0xff));
+ RUN(testSExt8(0x100));
+ RUN(testSExt8Fold(0));
+ RUN(testSExt8Fold(1));
+ RUN(testSExt8Fold(42));
+ RUN(testSExt8Fold(-1));
+ RUN(testSExt8Fold(0xff));
+ RUN(testSExt8Fold(0x100));
+ RUN(testSExt8SExt8(0));
+ RUN(testSExt8SExt8(1));
+ RUN(testSExt8SExt8(42));
+ RUN(testSExt8SExt8(-1));
+ RUN(testSExt8SExt8(0xff));
+ RUN(testSExt8SExt8(0x100));
+ RUN(testSExt8SExt16(0));
+ RUN(testSExt8SExt16(1));
+ RUN(testSExt8SExt16(42));
+ RUN(testSExt8SExt16(-1));
+ RUN(testSExt8SExt16(0xff));
+ RUN(testSExt8SExt16(0x100));
+ RUN(testSExt8SExt16(0xffff));
+ RUN(testSExt8SExt16(0x10000));
+ RUN(testSExt8BitAnd(0, 0));
+ RUN(testSExt8BitAnd(1, 0));
+ RUN(testSExt8BitAnd(42, 0));
+ RUN(testSExt8BitAnd(-1, 0));
+ RUN(testSExt8BitAnd(0xff, 0));
+ RUN(testSExt8BitAnd(0x100, 0));
+ RUN(testSExt8BitAnd(0xffff, 0));
+ RUN(testSExt8BitAnd(0x10000, 0));
+ RUN(testSExt8BitAnd(0, 0xf));
+ RUN(testSExt8BitAnd(1, 0xf));
+ RUN(testSExt8BitAnd(42, 0xf));
+ RUN(testSExt8BitAnd(-1, 0xf));
+ RUN(testSExt8BitAnd(0xff, 0xf));
+ RUN(testSExt8BitAnd(0x100, 0xf));
+ RUN(testSExt8BitAnd(0xffff, 0xf));
+ RUN(testSExt8BitAnd(0x10000, 0xf));
+ RUN(testSExt8BitAnd(0, 0xff));
+ RUN(testSExt8BitAnd(1, 0xff));
+ RUN(testSExt8BitAnd(42, 0xff));
+ RUN(testSExt8BitAnd(-1, 0xff));
+ RUN(testSExt8BitAnd(0xff, 0xff));
+ RUN(testSExt8BitAnd(0x100, 0xff));
+ RUN(testSExt8BitAnd(0xffff, 0xff));
+ RUN(testSExt8BitAnd(0x10000, 0xff));
+ RUN(testSExt8BitAnd(0, 0x80));
+ RUN(testSExt8BitAnd(1, 0x80));
+ RUN(testSExt8BitAnd(42, 0x80));
+ RUN(testSExt8BitAnd(-1, 0x80));
+ RUN(testSExt8BitAnd(0xff, 0x80));
+ RUN(testSExt8BitAnd(0x100, 0x80));
+ RUN(testSExt8BitAnd(0xffff, 0x80));
+ RUN(testSExt8BitAnd(0x10000, 0x80));
+ RUN(testBitAndSExt8(0, 0xf));
+ RUN(testBitAndSExt8(1, 0xf));
+ RUN(testBitAndSExt8(42, 0xf));
+ RUN(testBitAndSExt8(-1, 0xf));
+ RUN(testBitAndSExt8(0xff, 0xf));
+ RUN(testBitAndSExt8(0x100, 0xf));
+ RUN(testBitAndSExt8(0xffff, 0xf));
+ RUN(testBitAndSExt8(0x10000, 0xf));
+ RUN(testBitAndSExt8(0, 0xff));
+ RUN(testBitAndSExt8(1, 0xff));
+ RUN(testBitAndSExt8(42, 0xff));
+ RUN(testBitAndSExt8(-1, 0xff));
+ RUN(testBitAndSExt8(0xff, 0xff));
+ RUN(testBitAndSExt8(0x100, 0xff));
+ RUN(testBitAndSExt8(0xffff, 0xff));
+ RUN(testBitAndSExt8(0x10000, 0xff));
+ RUN(testBitAndSExt8(0, 0xfff));
+ RUN(testBitAndSExt8(1, 0xfff));
+ RUN(testBitAndSExt8(42, 0xfff));
+ RUN(testBitAndSExt8(-1, 0xfff));
+ RUN(testBitAndSExt8(0xff, 0xfff));
+ RUN(testBitAndSExt8(0x100, 0xfff));
+ RUN(testBitAndSExt8(0xffff, 0xfff));
+ RUN(testBitAndSExt8(0x10000, 0xfff));
+
+ RUN(testSExt16(0));
+ RUN(testSExt16(1));
+ RUN(testSExt16(42));
+ RUN(testSExt16(-1));
+ RUN(testSExt16(0xffff));
+ RUN(testSExt16(0x10000));
+ RUN(testSExt16Fold(0));
+ RUN(testSExt16Fold(1));
+ RUN(testSExt16Fold(42));
+ RUN(testSExt16Fold(-1));
+ RUN(testSExt16Fold(0xffff));
+ RUN(testSExt16Fold(0x10000));
+ RUN(testSExt16SExt8(0));
+ RUN(testSExt16SExt8(1));
+ RUN(testSExt16SExt8(42));
+ RUN(testSExt16SExt8(-1));
+ RUN(testSExt16SExt8(0xffff));
+ RUN(testSExt16SExt8(0x10000));
+ RUN(testSExt16SExt16(0));
+ RUN(testSExt16SExt16(1));
+ RUN(testSExt16SExt16(42));
+ RUN(testSExt16SExt16(-1));
+ RUN(testSExt16SExt16(0xffff));
+ RUN(testSExt16SExt16(0x10000));
+ RUN(testSExt16SExt16(0xffffff));
+ RUN(testSExt16SExt16(0x1000000));
+ RUN(testSExt16BitAnd(0, 0));
+ RUN(testSExt16BitAnd(1, 0));
+ RUN(testSExt16BitAnd(42, 0));
+ RUN(testSExt16BitAnd(-1, 0));
+ RUN(testSExt16BitAnd(0xffff, 0));
+ RUN(testSExt16BitAnd(0x10000, 0));
+ RUN(testSExt16BitAnd(0xffffff, 0));
+ RUN(testSExt16BitAnd(0x1000000, 0));
+ RUN(testSExt16BitAnd(0, 0xf));
+ RUN(testSExt16BitAnd(1, 0xf));
+ RUN(testSExt16BitAnd(42, 0xf));
+ RUN(testSExt16BitAnd(-1, 0xf));
+ RUN(testSExt16BitAnd(0xffff, 0xf));
+ RUN(testSExt16BitAnd(0x10000, 0xf));
+ RUN(testSExt16BitAnd(0xffffff, 0xf));
+ RUN(testSExt16BitAnd(0x1000000, 0xf));
+ RUN(testSExt16BitAnd(0, 0xffff));
+ RUN(testSExt16BitAnd(1, 0xffff));
+ RUN(testSExt16BitAnd(42, 0xffff));
+ RUN(testSExt16BitAnd(-1, 0xffff));
+ RUN(testSExt16BitAnd(0xffff, 0xffff));
+ RUN(testSExt16BitAnd(0x10000, 0xffff));
+ RUN(testSExt16BitAnd(0xffffff, 0xffff));
+ RUN(testSExt16BitAnd(0x1000000, 0xffff));
+ RUN(testSExt16BitAnd(0, 0x8000));
+ RUN(testSExt16BitAnd(1, 0x8000));
+ RUN(testSExt16BitAnd(42, 0x8000));
+ RUN(testSExt16BitAnd(-1, 0x8000));
+ RUN(testSExt16BitAnd(0xffff, 0x8000));
+ RUN(testSExt16BitAnd(0x10000, 0x8000));
+ RUN(testSExt16BitAnd(0xffffff, 0x8000));
+ RUN(testSExt16BitAnd(0x1000000, 0x8000));
+ RUN(testBitAndSExt16(0, 0xf));
+ RUN(testBitAndSExt16(1, 0xf));
+ RUN(testBitAndSExt16(42, 0xf));
+ RUN(testBitAndSExt16(-1, 0xf));
+ RUN(testBitAndSExt16(0xffff, 0xf));
+ RUN(testBitAndSExt16(0x10000, 0xf));
+ RUN(testBitAndSExt16(0xffffff, 0xf));
+ RUN(testBitAndSExt16(0x1000000, 0xf));
+ RUN(testBitAndSExt16(0, 0xffff));
+ RUN(testBitAndSExt16(1, 0xffff));
+ RUN(testBitAndSExt16(42, 0xffff));
+ RUN(testBitAndSExt16(-1, 0xffff));
+ RUN(testBitAndSExt16(0xffff, 0xffff));
+ RUN(testBitAndSExt16(0x10000, 0xffff));
+ RUN(testBitAndSExt16(0xffffff, 0xffff));
+ RUN(testBitAndSExt16(0x1000000, 0xffff));
+ RUN(testBitAndSExt16(0, 0xfffff));
+ RUN(testBitAndSExt16(1, 0xfffff));
+ RUN(testBitAndSExt16(42, 0xfffff));
+ RUN(testBitAndSExt16(-1, 0xfffff));
+ RUN(testBitAndSExt16(0xffff, 0xfffff));
+ RUN(testBitAndSExt16(0x10000, 0xfffff));
+ RUN(testBitAndSExt16(0xffffff, 0xfffff));
+ RUN(testBitAndSExt16(0x1000000, 0xfffff));
+
+ RUN(testSExt32BitAnd(0, 0));
+ RUN(testSExt32BitAnd(1, 0));
+ RUN(testSExt32BitAnd(42, 0));
+ RUN(testSExt32BitAnd(-1, 0));
+ RUN(testSExt32BitAnd(0x80000000, 0));
+ RUN(testSExt32BitAnd(0, 0xf));
+ RUN(testSExt32BitAnd(1, 0xf));
+ RUN(testSExt32BitAnd(42, 0xf));
+ RUN(testSExt32BitAnd(-1, 0xf));
+ RUN(testSExt32BitAnd(0x80000000, 0xf));
+ RUN(testSExt32BitAnd(0, 0x80000000));
+ RUN(testSExt32BitAnd(1, 0x80000000));
+ RUN(testSExt32BitAnd(42, 0x80000000));
+ RUN(testSExt32BitAnd(-1, 0x80000000));
+ RUN(testSExt32BitAnd(0x80000000, 0x80000000));
+ RUN(testBitAndSExt32(0, 0xf));
+ RUN(testBitAndSExt32(1, 0xf));
+ RUN(testBitAndSExt32(42, 0xf));
+ RUN(testBitAndSExt32(-1, 0xf));
+ RUN(testBitAndSExt32(0xffff, 0xf));
+ RUN(testBitAndSExt32(0x10000, 0xf));
+ RUN(testBitAndSExt32(0xffffff, 0xf));
+ RUN(testBitAndSExt32(0x1000000, 0xf));
+ RUN(testBitAndSExt32(0, 0xffff00000000llu));
+ RUN(testBitAndSExt32(1, 0xffff00000000llu));
+ RUN(testBitAndSExt32(42, 0xffff00000000llu));
+ RUN(testBitAndSExt32(-1, 0xffff00000000llu));
+ RUN(testBitAndSExt32(0x80000000, 0xffff00000000llu));
+
RUN(testBasicSelect());
RUN(testSelectTest());
RUN(testSelectCompareDouble());
@@ -8587,6 +9193,7 @@
for (ThreadIdentifier thread : threads)
waitForThreadCompletion(thread);
+ crashLock.lock();
}
} // anonymous namespace