2019-08-01 01:01:53 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015-2019 Apple Inc. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "testb3.h"
|
|
|
|
|
|
|
|
#if ENABLE(B3_JIT)
|
|
|
|
|
Add Pre/Post-Indexed Address Mode to Air for ARM64
https://bugs.webkit.org/show_bug.cgi?id=228047
Reviewed by Phil Pizlo.
Pre-indexed addressing means that the address is the sum of the value in the 64-bit base register
and an offset, and the address is then written back to the base register. And post-indexed
addressing means that the address is the value in the 64-bit base register, and the sum of the
address and the offset is then written back to the base register. They are relatively common for
loops to iterate over an array by increasing/decreasing a pointer into the array at each iteration.
With such an addressing mode, the instruction selector can merge the increment and access the array.
#####################################
## Pre-Index Address Mode For Load ##
#####################################
LDR Wt, [Xn, #imm]!
In B3 Reduction Strength, since we have this reduction rule:
Turn this: Load(Add(address, offset1), offset = offset2)
Into this: Load(address, offset = offset1 + offset2)
Then, the equivalent pattern is:
address = Add(base, offset)
...
memory = Load(base, offset)
First, we convert it to the canonical form:
address = Add(base, offset)
newMemory = Load(base, offset) // move the memory to just after the address
...
memory = Identity(newMemory)
Next, lower to Air:
Move %base, %address
Move (%address, prefix(offset)), %newMemory
######################################
## Post-Index Address Mode For Load ##
######################################
LDR Wt, [Xn], #imm
Then, the equivalent pattern is:
memory = Load(base, 0)
...
address = Add(base, offset)
First, we convert it to the canonical form:
newOffset = Constant
newAddress = Add(base, offset)
memory = Load(base, 0) // move the offset and address to just before the memory
...
offset = Identity(newOffset)
address = Identity(newAddress)
Next, lower to Air:
Move %base, %newAddress
Move (%newAddress, postfix(offset)), %memory
#############################
## Pattern Match Algorithm ##
#############################
To detect the pattern for prefix/postfix increment address is tricky due to the structure in B3 IR. The
algorithm used in this patch is to collect the first valid values (add/load), then search for any
paired value (load/add) to match all of them. In worst case, the runtime complexity is O(n^2)
when n is the number of all values.
After collecting two sets of candidates, we match the prefix incremental address first since it seems
more beneficial to the compiler (shown in the next section). And then, go for the postfix one.
##############################################
## Test for Pre/Post-Increment Address Mode ##
##############################################
Given Loop with Pre-Increment:
int64_t ldr_pre(int64_t *p) {
int64_t res = 0;
while (res < 10)
res += *++p;
return res;
}
B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
Int64 b@0 = Const64(0)
Int64 b@2 = ArgumentReg(%x0)
Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
Int64 b@18 = Phi(ReadsLocalState)
Int64 b@19 = Phi(ReadsLocalState)
Int64 b@7 = Const64(10)
Int32 b@8 = AboveEqual(b@18, $10(b@7))
Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
Int64 b@10 = Const64(8)
Int64 b@11 = Add(b@19, $8(b@10))
Int64 b@13 = Load(b@11, ControlDependent|Reads:Top)
Int64 b@14 = Add(b@18, b@13)
Void b@22 = Upsilon(b@14, ^18, WritesLocalState)
Void b@23 = Upsilon(b@11, ^19, WritesLocalState)
Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
Void b@17 = Return(b@18, Terminal)
Variables:
Int64 var0
Int64 var1
------------------------------------------------------
W/O Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
Move $8, %x3, $8(b@12)
Add64 $8, %x0, %x1, b@11
Move (%x0,%x3), %x0, b@13
Add64 %x0, %x2, %x2, b@14
Move %x1, %x0, b@23
Jump b@16
Successors: #1
...
------------------------------------------------------
W/ Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
MoveWithIncrement64 (%x0,Pre($8)), %x2, b@13
Add64 %x2, %x1, %x1, b@14
Jump b@16
Successors: #1
...
------------------------------------------------------
Given Loop with Post-Increment:
int64_t ldr_pre(int64_t *p) {
int64_t res = 0;
while (res < 10)
res += *p++;
return res;
}
B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
Int64 b@0 = Const64(0)
Int64 b@2 = ArgumentReg(%x0)
Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
Int64 b@18 = Phi(ReadsLocalState)
Int64 b@19 = Phi(ReadsLocalState)
Int64 b@7 = Const64(10)
Int32 b@8 = AboveEqual(b@18, $10(b@7))
Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
Int64 b@10 = Load(b@19, ControlDependent|Reads:Top)
Int64 b@11 = Add(b@18, b@10)
Int64 b@12 = Const64(8)
Int64 b@13 = Add(b@19, $8(b@12))
Void b@22 = Upsilon(b@11, ^18, WritesLocalState)
Void b@23 = Upsilon(b@13, ^19, WritesLocalState)
Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
Void b@17 = Return(b@18, Terminal)
Variables:
Int64 var0
Int64 var1
------------------------------------------------------
W/O Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
Move (%x0), %x2, b@10
Add64 %x2, %x1, %x1, b@11
Add64 $8, %x0, %x0, b@13
Jump b@16
Successors: #1
...
------------------------------------------------------
W/ Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
MoveWithIncrement64 (%x0,Post($8)), %x2, b@10
Add64 %x2, %x1, %x1, b@11
Jump b@16
Successors: #1
...
------------------------------------------------------
* Sources.txt:
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::PreIndexAddress::PreIndexAddress):
(JSC::AbstractMacroAssembler::PostIndexAddress::PostIndexAddress):
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::load64):
(JSC::MacroAssemblerARM64::load32):
(JSC::MacroAssemblerARM64::store64):
(JSC::MacroAssemblerARM64::store32):
* assembler/testmasm.cpp:
(JSC::testStorePrePostIndex32):
(JSC::testStorePrePostIndex64):
(JSC::testLoadPrePostIndex32):
(JSC::testLoadPrePostIndex64):
* b3/B3CanonicalizePrePostIncrements.cpp: Added.
(JSC::B3::canonicalizePrePostIncrements):
* b3/B3CanonicalizePrePostIncrements.h: Copied from Source/JavaScriptCore/b3/B3ValueKeyInlines.h.
* b3/B3Generate.cpp:
(JSC::B3::generateToAir):
* b3/B3LowerToAir.cpp:
* b3/B3ValueKey.h:
* b3/B3ValueKeyInlines.h:
(JSC::B3::ValueKey::ValueKey):
* b3/air/AirArg.cpp:
(JSC::B3::Air::Arg::jsHash const):
(JSC::B3::Air::Arg::dump const):
(WTF::printInternal):
* b3/air/AirArg.h:
(JSC::B3::Air::Arg::preIndex):
(JSC::B3::Air::Arg::postIndex):
(JSC::B3::Air::Arg::isPreIndex const):
(JSC::B3::Air::Arg::isPostIndex const):
(JSC::B3::Air::Arg::isMemory const):
(JSC::B3::Air::Arg::base const):
(JSC::B3::Air::Arg::offset const):
(JSC::B3::Air::Arg::isGP const):
(JSC::B3::Air::Arg::isFP const):
(JSC::B3::Air::Arg::isValidPreIndexForm):
(JSC::B3::Air::Arg::isValidPostIndexForm):
(JSC::B3::Air::Arg::isValidForm const):
(JSC::B3::Air::Arg::forEachTmpFast):
(JSC::B3::Air::Arg::forEachTmp):
(JSC::B3::Air::Arg::asPreIndexAddress const):
(JSC::B3::Air::Arg::asPostIndexAddress const):
* b3/air/AirOpcode.opcodes:
* b3/air/opcode_generator.rb:
* b3/testb3.h:
* b3/testb3_3.cpp:
(testLoadPreIndex32):
(testLoadPreIndex64):
(testLoadPostIndex32):
(testLoadPostIndex64):
(addShrTests):
* jit/ExecutableAllocator.cpp:
(JSC::jitWriteThunkGenerator):
Canonical link: https://commits.webkit.org/240125@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280493 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-07-30 20:44:47 +00:00
|
|
|
|
|
|
|
void testLoadPreIndex32()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int32_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
|
|
|
int32_t* ptr = nums;
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* loopTest = proc.addBlock();
|
|
|
|
BasicBlock* loopBody = proc.addBlock();
|
|
|
|
BasicBlock* done = proc.addBlock();
|
|
|
|
|
|
|
|
Variable* r = proc.addVariable(Int32);
|
|
|
|
Variable* p = proc.addVariable(Int64);
|
|
|
|
|
|
|
|
// ---------------------- Root_Block
|
|
|
|
// r1 = 0
|
|
|
|
// Upsilon(r1, ^r2)
|
|
|
|
// p1 = addr
|
|
|
|
// Upsilon(p1, ^p2)
|
|
|
|
Value* r1 = root->appendIntConstant(proc, Origin(), Int32, 0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
|
|
|
|
Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
|
|
|
|
root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Test_Block
|
|
|
|
// loop:
|
|
|
|
// p2 = Phi()
|
|
|
|
// r2 = Phi()
|
|
|
|
// if r2 >= 10 goto done
|
|
|
|
Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
|
|
|
|
Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
|
|
|
|
Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const32Value>(proc, Origin(), 10));
|
|
|
|
loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Body_Block
|
|
|
|
// p3 = p2 + 1
|
|
|
|
// Upsilon(p3, ^p2)
|
|
|
|
// r3 = r2 + load(p3)
|
|
|
|
// Upsilon(r3, ^r2)
|
|
|
|
// goto loop
|
|
|
|
Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 4));
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
|
|
|
|
Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int32, Origin(), p3));
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
|
|
|
|
loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Done_Block
|
|
|
|
// done:
|
|
|
|
// return r2
|
|
|
|
done->appendNewControlValue(proc, Return, Origin(), r2);
|
|
|
|
|
|
|
|
proc.resetReachability();
|
|
|
|
validate(proc);
|
|
|
|
fixSSA(proc);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "#4]!");
|
|
|
|
|
|
|
|
auto test = [&] () -> int32_t {
|
|
|
|
int32_t r = 0;
|
|
|
|
while (r < 10)
|
|
|
|
r += *++ptr;
|
|
|
|
return r;
|
|
|
|
};
|
|
|
|
|
|
|
|
CHECK_EQ(invoke<int32_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
|
|
|
|
}
|
|
|
|
|
|
|
|
void testLoadPreIndex64()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int64_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
|
|
|
int64_t* ptr = nums;
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* loopTest = proc.addBlock();
|
|
|
|
BasicBlock* loopBody = proc.addBlock();
|
|
|
|
BasicBlock* done = proc.addBlock();
|
|
|
|
|
|
|
|
Variable* r = proc.addVariable(Int64);
|
|
|
|
Variable* p = proc.addVariable(Int64);
|
|
|
|
|
|
|
|
// ---------------------- Root_Block
|
|
|
|
// r1 = 0
|
|
|
|
// Upsilon(r1, ^r2)
|
|
|
|
// p1 = addr
|
|
|
|
// Upsilon(p1, ^p2)
|
|
|
|
Value* r1 = root->appendIntConstant(proc, Origin(), Int64, 0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
|
|
|
|
Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
|
|
|
|
root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Test_Block
|
|
|
|
// loop:
|
|
|
|
// p2 = Phi()
|
|
|
|
// r2 = Phi()
|
|
|
|
// if r2 >= 10 goto done
|
|
|
|
Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
|
|
|
|
Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
|
|
|
|
Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const64Value>(proc, Origin(), 10));
|
|
|
|
loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Body_Block
|
|
|
|
// p3 = p2 + 1
|
|
|
|
// Upsilon(p3, ^p2)
|
|
|
|
// r3 = r2 + load(p3)
|
|
|
|
// Upsilon(r3, ^r2)
|
|
|
|
// goto loop
|
|
|
|
Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 8));
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
|
|
|
|
Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int64, Origin(), p3));
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
|
|
|
|
loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Done_Block
|
|
|
|
// done:
|
|
|
|
// return r2
|
|
|
|
done->appendNewControlValue(proc, Return, Origin(), r2);
|
|
|
|
|
|
|
|
proc.resetReachability();
|
|
|
|
validate(proc);
|
|
|
|
fixSSA(proc);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "#8]!");
|
|
|
|
|
|
|
|
auto test = [&] () -> int64_t {
|
|
|
|
int64_t r = 0;
|
|
|
|
while (r < 10)
|
|
|
|
r += *++ptr;
|
|
|
|
return r;
|
|
|
|
};
|
|
|
|
|
|
|
|
CHECK_EQ(invoke<int64_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
|
|
|
|
}
|
|
|
|
|
|
|
|
void testLoadPostIndex32()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int32_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
|
|
|
int32_t* ptr = nums;
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* loopTest = proc.addBlock();
|
|
|
|
BasicBlock* loopBody = proc.addBlock();
|
|
|
|
BasicBlock* done = proc.addBlock();
|
|
|
|
|
|
|
|
Variable* r = proc.addVariable(Int32);
|
|
|
|
Variable* p = proc.addVariable(Int64);
|
|
|
|
|
|
|
|
// ---------------------- Root_Block
|
|
|
|
// r1 = 0
|
|
|
|
// Upsilon(r1, ^r2)
|
|
|
|
// p1 = addr
|
|
|
|
// Upsilon(p1, ^p2)
|
|
|
|
Value* r1 = root->appendIntConstant(proc, Origin(), Int32, 0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
|
|
|
|
Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
|
|
|
|
root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Test_Block
|
|
|
|
// loop:
|
|
|
|
// p2 = Phi()
|
|
|
|
// r2 = Phi()
|
|
|
|
// if r2 >= 10 goto done
|
|
|
|
Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
|
|
|
|
Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
|
|
|
|
Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const32Value>(proc, Origin(), 10));
|
|
|
|
loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Body_Block
|
|
|
|
// r3 = r2 + load(p2)
|
|
|
|
// p3 = p2 + 1
|
|
|
|
// Upsilon(r3, ^r2)
|
|
|
|
// Upsilon(p3, ^p2)
|
|
|
|
// goto loop
|
|
|
|
Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int32, Origin(), p2));
|
|
|
|
Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 4));
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
|
|
|
|
loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Done_Block
|
|
|
|
// done:
|
|
|
|
// return r2
|
|
|
|
done->appendNewControlValue(proc, Return, Origin(), r2);
|
|
|
|
|
|
|
|
proc.resetReachability();
|
|
|
|
validate(proc);
|
|
|
|
fixSSA(proc);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "], #4");
|
|
|
|
|
|
|
|
auto test = [&] () -> int32_t {
|
|
|
|
int32_t r = 0;
|
|
|
|
while (r < 10)
|
|
|
|
r += *ptr++;
|
|
|
|
return r;
|
|
|
|
};
|
|
|
|
|
|
|
|
CHECK_EQ(invoke<int32_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
|
|
|
|
}
|
|
|
|
|
|
|
|
void testLoadPostIndex64()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int64_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
|
|
|
int64_t* ptr = nums;
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* loopTest = proc.addBlock();
|
|
|
|
BasicBlock* loopBody = proc.addBlock();
|
|
|
|
BasicBlock* done = proc.addBlock();
|
|
|
|
|
|
|
|
Variable* r = proc.addVariable(Int64);
|
|
|
|
Variable* p = proc.addVariable(Int64);
|
|
|
|
|
|
|
|
// ---------------------- Root_Block
|
|
|
|
// r1 = 0
|
|
|
|
// Upsilon(r1, ^r2)
|
|
|
|
// p1 = addr
|
|
|
|
// Upsilon(p1, ^p2)
|
|
|
|
Value* r1 = root->appendIntConstant(proc, Origin(), Int64, 0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
|
|
|
|
Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
|
|
|
|
root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Test_Block
|
|
|
|
// loop:
|
|
|
|
// p2 = Phi()
|
|
|
|
// r2 = Phi()
|
|
|
|
// if r2 >= 10 goto done
|
|
|
|
Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
|
|
|
|
Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
|
|
|
|
Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const64Value>(proc, Origin(), 10));
|
|
|
|
loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
|
|
|
|
|
|
|
|
// ---------------------- Loop_Body_Block
|
|
|
|
// r3 = r2 + load(p2)
|
|
|
|
// p3 = p2 + 1
|
|
|
|
// Upsilon(r3, ^r2)
|
|
|
|
// Upsilon(p3, ^p2)
|
|
|
|
// goto loop
|
|
|
|
Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int64, Origin(), p2));
|
|
|
|
Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 8));
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
|
|
|
|
loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
|
|
|
|
loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
|
|
|
|
|
|
|
|
// ---------------------- Done_Block
|
|
|
|
// done:
|
|
|
|
// return r2
|
|
|
|
done->appendNewControlValue(proc, Return, Origin(), r2);
|
|
|
|
|
|
|
|
proc.resetReachability();
|
|
|
|
validate(proc);
|
|
|
|
fixSSA(proc);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "], #8");
|
|
|
|
|
|
|
|
auto test = [&] () -> int64_t {
|
|
|
|
int64_t r = 0;
|
|
|
|
while (r < 10)
|
|
|
|
r += *ptr++;
|
|
|
|
return r;
|
|
|
|
};
|
|
|
|
|
|
|
|
CHECK_EQ(invoke<int64_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
|
|
|
|
}
|
|
|
|
|
[ARM64] Add Pre/Post-Indexed Address Mode to Air for ARM64 (Store Instruction)
https://bugs.webkit.org/show_bug.cgi?id=228538
Reviewed by Filip Pizlo.
The previous patch has introduced the pre/post-index address mode for Load instruction,
which benefits loop program. Here, this patch adds the corresponding mode for Store
instruction. Store Register (immediate) stores a word or a doubleword from a register
to memory. The address that is used for the store is calculated from a base register
and an immediate offset.
######################################
## Pre-Index Address Mode For Store ##
######################################
STR Wt, [Xn, #imm]!
In B3 Reduction Strength, since we have this reduction rule:
Turns this: Store(value, Add(address, offset1), offset = offset2)
Into this: Store(value, address, offset = offset1 + offset2)
The equivalent pattern is:
address = Add(base, offset)
...
memory = Store(value, base, offset)
Here, we only consider the pattern:
address = Add(base, offset)
memory = Store(value, base, offset)
And, this patch directly treats it as the canonical form. Ideally, we should move
memory to just after the address like what we did for PreIndex Load in the previous
patch. But, we cannot provide a better way to do that since the value may be
used between address and memory. To move value upward, we must move all descendants
of the value along with it to prevent B3 IR index violations, which is risky and expensive.
Next, lower to Air:
Move %base, %address
Move %value, (%address, prefix(offset))
######################################
## Post-Index Address Mode For Load ##
######################################
STR Wt, [Xn], #imm
Then, the equivalent pattern is:
memory = Store(value, base, 0)
...
address = Add(base, offset)
First, we convert it to the canonical form:
newOffset = Constant
newAddress = Add(base, offset)
memory = Store(value, base, 0) // move the offset and address to just before the memory
...
offset = Identity(newOffset)
address = Identity(newAddress)
Next, lower to Air:
Move %base, %newAddress
Move %value, (%newAddress, postfix(offset))
##############################################
## Test for Pre/Post-Increment Address Mode ##
##############################################
B3 IR:
------------------------------------------------------
Int64 b@0 = ArgumentReg(%x0)
Int64 b@1 = ArgumentReg(%x1)
Int64 b@2 = Const64(8)
Int64 b@3 = Add(b@0, $8(b@2))
Void b@4 = Store(b@1, b@3, ControlDependent|Writes:Top)
Void b@5 = Return(b@3, Terminal)
------------------------------------------------------
W/O Pre-Increment Address Mode:
------------------------------------------------------
Move %x0, %x2, b@0
Add64 $8, %x2, %x0, b@3
Move %x1, 8(%x2), b@4
Ret64 %x0, b@5
------------------------------------------------------
W/ Pre-Increment Address Mode:
------------------------------------------------------
MoveWithIncrement64 %x1, (%x0,Pre($8)), b@4
Ret64 %x0, b@5
------------------------------------------------------
B3 IR:
------------------------------------------------------
Int64 b@0 = ArgumentReg(%x0)
Int64 b@1 = ArgumentReg(%x1)
Void b@2 = Store(b@1, b@0, ControlDependent|Writes:Top)
Int64 b@3 = Const64(8)
Int64 b@4 = Add(b@0, $8(b@3))
Void b@5 = Return(b@4, Terminal)
------------------------------------------------------
W/O Post-Increment Address Mode:
------------------------------------------------------
Move %x1, (%x0), b@2
Add64 $8, %x0, %x0, b@4
Ret64 %x0, b@5
------------------------------------------------------
W/ Post-Increment Address Mode:
------------------------------------------------------
MoveWithIncrement64 %x1, (%x0,Post($8)), b@2
Ret64 %x0, b@5
------------------------------------------------------
* b3/B3CanonicalizePrePostIncrements.cpp:
(JSC::B3::canonicalizePrePostIncrements):
* b3/B3LowerToAir.cpp:
* b3/testb3.h:
* b3/testb3_3.cpp:
(testStorePreIndex32):
(testStorePreIndex64):
(testStorePostIndex32):
(testStorePostIndex64):
(addShrTests):
Canonical link: https://commits.webkit.org/240524@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@281062 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-08-15 17:41:03 +00:00
|
|
|
void testStorePreIndex32()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int32_t nums[] = { 1, 2, 3 };
|
|
|
|
int32_t* ptr = &nums[1];
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* value = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* offset = root->appendNew<Const64Value>(proc, Origin(), 4);
|
|
|
|
Value* preIncrement = root->appendNew<Value>(proc, Add, Origin(), address, offset);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), value, preIncrement);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), preIncrement);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "#4]!");
|
|
|
|
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
|
|
|
|
ptr = bitwise_cast<int32_t*>(res);
|
|
|
|
CHECK_EQ(nums[2], *ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStorePreIndex64()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int64_t nums[] = { 1, 2, 3 };
|
|
|
|
int64_t* ptr = &nums[1];
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* value = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* offset = root->appendNew<Const64Value>(proc, Origin(), 8);
|
|
|
|
Value* preIncrement = root->appendNew<Value>(proc, Add, Origin(), address, offset);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), value, preIncrement);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), preIncrement);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
|
|
|
|
ptr = bitwise_cast<int64_t*>(res);
|
|
|
|
CHECK_EQ(nums[2], *ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStorePostIndex32()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int32_t nums[] = { 1, 2, 3 };
|
|
|
|
int32_t* ptr = &nums[1];
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* value = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), value, address);
|
|
|
|
Value* offset = root->appendNew<Const64Value>(proc, Origin(), 4);
|
|
|
|
Value* preIncrement = root->appendNew<Value>(proc, Add, Origin(), address, offset);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), preIncrement);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "], #4");
|
|
|
|
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
|
|
|
|
ptr = bitwise_cast<int32_t*>(res);
|
|
|
|
CHECK_EQ(nums[1], 4);
|
|
|
|
CHECK_EQ(nums[2], *ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStorePostIndex64()
|
|
|
|
{
|
|
|
|
if (Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int64_t nums[] = { 1, 2, 3 };
|
|
|
|
int64_t* ptr = &nums[1];
|
|
|
|
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* value = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), value, address);
|
|
|
|
Value* offset = root->appendNew<Const64Value>(proc, Origin(), 8);
|
|
|
|
Value* preIncrement = root->appendNew<Value>(proc, Add, Origin(), address, offset);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), preIncrement);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "], #8");
|
|
|
|
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
|
|
|
|
ptr = bitwise_cast<int64_t*>(res);
|
|
|
|
CHECK_EQ(nums[1], 4);
|
|
|
|
CHECK_EQ(nums[2], *ptr);
|
|
|
|
}
|
Add Pre/Post-Indexed Address Mode to Air for ARM64
https://bugs.webkit.org/show_bug.cgi?id=228047
Reviewed by Phil Pizlo.
Pre-indexed addressing means that the address is the sum of the value in the 64-bit base register
and an offset, and the address is then written back to the base register. And post-indexed
addressing means that the address is the value in the 64-bit base register, and the sum of the
address and the offset is then written back to the base register. They are relatively common for
loops to iterate over an array by increasing/decreasing a pointer into the array at each iteration.
With such an addressing mode, the instruction selector can merge the increment and access the array.
#####################################
## Pre-Index Address Mode For Load ##
#####################################
LDR Wt, [Xn, #imm]!
In B3 Reduction Strength, since we have this reduction rule:
Turn this: Load(Add(address, offset1), offset = offset2)
Into this: Load(address, offset = offset1 + offset2)
Then, the equivalent pattern is:
address = Add(base, offset)
...
memory = Load(base, offset)
First, we convert it to the canonical form:
address = Add(base, offset)
newMemory = Load(base, offset) // move the memory to just after the address
...
memory = Identity(newMemory)
Next, lower to Air:
Move %base, %address
Move (%address, prefix(offset)), %newMemory
######################################
## Post-Index Address Mode For Load ##
######################################
LDR Wt, [Xn], #imm
Then, the equivalent pattern is:
memory = Load(base, 0)
...
address = Add(base, offset)
First, we convert it to the canonical form:
newOffset = Constant
newAddress = Add(base, offset)
memory = Load(base, 0) // move the offset and address to just before the memory
...
offset = Identity(newOffset)
address = Identity(newAddress)
Next, lower to Air:
Move %base, %newAddress
Move (%newAddress, postfix(offset)), %memory
#############################
## Pattern Match Algorithm ##
#############################
To detect the pattern for prefix/postfix increment address is tricky due to the structure in B3 IR. The
algorithm used in this patch is to collect the first valid values (add/load), then search for any
paired value (load/add) to match all of them. In worst case, the runtime complexity is O(n^2)
when n is the number of all values.
After collecting two sets of candidates, we match the prefix incremental address first since it seems
more beneficial to the compiler (shown in the next section). And then, go for the postfix one.
##############################################
## Test for Pre/Post-Increment Address Mode ##
##############################################
Given Loop with Pre-Increment:
int64_t ldr_pre(int64_t *p) {
int64_t res = 0;
while (res < 10)
res += *++p;
return res;
}
B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
Int64 b@0 = Const64(0)
Int64 b@2 = ArgumentReg(%x0)
Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
Int64 b@18 = Phi(ReadsLocalState)
Int64 b@19 = Phi(ReadsLocalState)
Int64 b@7 = Const64(10)
Int32 b@8 = AboveEqual(b@18, $10(b@7))
Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
Int64 b@10 = Const64(8)
Int64 b@11 = Add(b@19, $8(b@10))
Int64 b@13 = Load(b@11, ControlDependent|Reads:Top)
Int64 b@14 = Add(b@18, b@13)
Void b@22 = Upsilon(b@14, ^18, WritesLocalState)
Void b@23 = Upsilon(b@11, ^19, WritesLocalState)
Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
Void b@17 = Return(b@18, Terminal)
Variables:
Int64 var0
Int64 var1
------------------------------------------------------
W/O Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
Move $8, %x3, $8(b@12)
Add64 $8, %x0, %x1, b@11
Move (%x0,%x3), %x0, b@13
Add64 %x0, %x2, %x2, b@14
Move %x1, %x0, b@23
Jump b@16
Successors: #1
...
------------------------------------------------------
W/ Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
MoveWithIncrement64 (%x0,Pre($8)), %x2, b@13
Add64 %x2, %x1, %x1, b@14
Jump b@16
Successors: #1
...
------------------------------------------------------
Given Loop with Post-Increment:
int64_t ldr_pre(int64_t *p) {
int64_t res = 0;
while (res < 10)
res += *p++;
return res;
}
B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
Int64 b@0 = Const64(0)
Int64 b@2 = ArgumentReg(%x0)
Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
Int64 b@18 = Phi(ReadsLocalState)
Int64 b@19 = Phi(ReadsLocalState)
Int64 b@7 = Const64(10)
Int32 b@8 = AboveEqual(b@18, $10(b@7))
Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
Int64 b@10 = Load(b@19, ControlDependent|Reads:Top)
Int64 b@11 = Add(b@18, b@10)
Int64 b@12 = Const64(8)
Int64 b@13 = Add(b@19, $8(b@12))
Void b@22 = Upsilon(b@11, ^18, WritesLocalState)
Void b@23 = Upsilon(b@13, ^19, WritesLocalState)
Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
Void b@17 = Return(b@18, Terminal)
Variables:
Int64 var0
Int64 var1
------------------------------------------------------
W/O Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
Move (%x0), %x2, b@10
Add64 %x2, %x1, %x1, b@11
Add64 $8, %x0, %x0, b@13
Jump b@16
Successors: #1
...
------------------------------------------------------
W/ Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
MoveWithIncrement64 (%x0,Post($8)), %x2, b@10
Add64 %x2, %x1, %x1, b@11
Jump b@16
Successors: #1
...
------------------------------------------------------
* Sources.txt:
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::PreIndexAddress::PreIndexAddress):
(JSC::AbstractMacroAssembler::PostIndexAddress::PostIndexAddress):
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::load64):
(JSC::MacroAssemblerARM64::load32):
(JSC::MacroAssemblerARM64::store64):
(JSC::MacroAssemblerARM64::store32):
* assembler/testmasm.cpp:
(JSC::testStorePrePostIndex32):
(JSC::testStorePrePostIndex64):
(JSC::testLoadPrePostIndex32):
(JSC::testLoadPrePostIndex64):
* b3/B3CanonicalizePrePostIncrements.cpp: Added.
(JSC::B3::canonicalizePrePostIncrements):
* b3/B3CanonicalizePrePostIncrements.h: Copied from Source/JavaScriptCore/b3/B3ValueKeyInlines.h.
* b3/B3Generate.cpp:
(JSC::B3::generateToAir):
* b3/B3LowerToAir.cpp:
* b3/B3ValueKey.h:
* b3/B3ValueKeyInlines.h:
(JSC::B3::ValueKey::ValueKey):
* b3/air/AirArg.cpp:
(JSC::B3::Air::Arg::jsHash const):
(JSC::B3::Air::Arg::dump const):
(WTF::printInternal):
* b3/air/AirArg.h:
(JSC::B3::Air::Arg::preIndex):
(JSC::B3::Air::Arg::postIndex):
(JSC::B3::Air::Arg::isPreIndex const):
(JSC::B3::Air::Arg::isPostIndex const):
(JSC::B3::Air::Arg::isMemory const):
(JSC::B3::Air::Arg::base const):
(JSC::B3::Air::Arg::offset const):
(JSC::B3::Air::Arg::isGP const):
(JSC::B3::Air::Arg::isFP const):
(JSC::B3::Air::Arg::isValidPreIndexForm):
(JSC::B3::Air::Arg::isValidPostIndexForm):
(JSC::B3::Air::Arg::isValidForm const):
(JSC::B3::Air::Arg::forEachTmpFast):
(JSC::B3::Air::Arg::forEachTmp):
(JSC::B3::Air::Arg::asPreIndexAddress const):
(JSC::B3::Air::Arg::asPostIndexAddress const):
* b3/air/AirOpcode.opcodes:
* b3/air/opcode_generator.rb:
* b3/testb3.h:
* b3/testb3_3.cpp:
(testLoadPreIndex32):
(testLoadPreIndex64):
(testLoadPostIndex32):
(testLoadPostIndex64):
(addShrTests):
* jit/ExecutableAllocator.cpp:
(JSC::jitWriteThunkGenerator):
Canonical link: https://commits.webkit.org/240125@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280493 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-07-30 20:44:47 +00:00
|
|
|
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
void testInsertSignedBitfieldInZero32()
|
|
|
|
{
|
|
|
|
if (JSC::Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
int32_t src = 0xffffffff;
|
|
|
|
Vector<int32_t> lsbs = { 1, 14, 29 };
|
|
|
|
Vector<int32_t> widths = { 30, 17, 2 };
|
|
|
|
|
|
|
|
// Test Pattern: (((src & mask1) ^ mask2) - mask2) << lsb
|
|
|
|
// where: mask1 = (1 << width) - 1
|
|
|
|
// mask2 = 1 << (width - 1)
|
|
|
|
auto test = [&] (int32_t lsb, int32_t mask1, int32_t mask2) -> int32_t {
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* srcValue = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
|
|
|
|
Value* mask1Value = root->appendNew<Const32Value>(proc, Origin(), mask1);
|
|
|
|
Value* mask2Value = root->appendNew<Const32Value>(proc, Origin(), mask2);
|
|
|
|
|
|
|
|
Value* andValue = root->appendNew<Value>(proc, BitAnd, Origin(), srcValue, mask1Value);
|
|
|
|
Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), andValue, mask2Value);
|
|
|
|
Value* subValue = root->appendNew<Value>(proc, Sub, Origin(), xorValue, mask2Value);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Shl, Origin(), subValue, lsbValue));
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "sbfiz");
|
|
|
|
return invoke<int32_t>(*code, src);
|
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < lsbs.size(); ++i) {
|
|
|
|
int32_t lsb = lsbs.at(i);
|
|
|
|
int32_t width = widths.at(i);
|
|
|
|
int32_t mask1 = (1 << width) - 1;
|
|
|
|
int32_t mask2 = 1 << (width - 1);
|
|
|
|
int32_t bfsx = ((src & mask1) ^ mask2) - mask2;
|
|
|
|
CHECK(test(lsb, mask1, mask2) == (bfsx << lsb));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testInsertSignedBitfieldInZero64()
|
|
|
|
{
|
|
|
|
if (JSC::Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
int64_t src = 0xffffffffffffffff;
|
2021-07-06 22:49:32 +00:00
|
|
|
Vector<int32_t> lsbs = { 1, 30, 62 };
|
|
|
|
Vector<int32_t> widths = { 62, 33, 1 };
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
|
|
|
|
// Test Pattern: ((src << amount) >> amount) << lsb
|
|
|
|
// where: amount = datasize - width
|
2021-07-06 22:49:32 +00:00
|
|
|
auto test = [&] (int32_t lsb, int32_t amount) -> int64_t {
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* srcValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
|
2021-07-06 22:49:32 +00:00
|
|
|
Value* amountValue = root->appendNew<Const32Value>(proc, Origin(), amount);
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
|
|
|
|
Value* signedRightShiftValue = root->appendNew<Value>(
|
|
|
|
proc, SShr, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Shl, Origin(), srcValue, amountValue),
|
|
|
|
amountValue);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Shl, Origin(), signedRightShiftValue, lsbValue));
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "sbfiz");
|
|
|
|
return invoke<uint64_t>(*code, src);
|
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < lsbs.size(); ++i) {
|
2021-07-06 22:49:32 +00:00
|
|
|
int32_t lsb = lsbs.at(i);
|
|
|
|
int32_t width = widths.at(i);
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
int64_t amount = CHAR_BIT * sizeof(src) - width;
|
|
|
|
int64_t bfsx = (src << amount) >> amount;
|
|
|
|
CHECK(test(lsb, amount) == (bfsx << lsb));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testExtractSignedBitfield32()
|
|
|
|
{
|
|
|
|
if (JSC::Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
int32_t src = 0xffffffff;
|
|
|
|
Vector<int32_t> lsbs = { 1, 14, 29 };
|
|
|
|
Vector<int32_t> widths = { 30, 17, 2 };
|
|
|
|
|
|
|
|
// Test Pattern: (((src >> lsb) & mask1) ^ mask2) - mask2
|
|
|
|
// where: mask1 = (1 << width) - 1
|
|
|
|
// mask2 = 1 << (width - 1)
|
|
|
|
auto test = [&] (int32_t lsb, int32_t mask1, int32_t mask2) -> int32_t {
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* srcValue = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
|
|
|
|
Value* mask1Value = root->appendNew<Const32Value>(proc, Origin(), mask1);
|
|
|
|
Value* mask2Value = root->appendNew<Const32Value>(proc, Origin(), mask2);
|
|
|
|
|
|
|
|
Value* shiftValue = root->appendNew<Value>(proc, SShr, Origin(), srcValue, lsbValue);
|
|
|
|
Value* andValue = root->appendNew<Value>(proc, BitAnd, Origin(), shiftValue, mask1Value);
|
|
|
|
Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), andValue, mask2Value);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Sub, Origin(), xorValue, mask2Value));
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "sbfx");
|
|
|
|
return invoke<int32_t>(*code, src);
|
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < lsbs.size(); ++i) {
|
|
|
|
int32_t lsb = lsbs.at(i);
|
|
|
|
int32_t width = widths.at(i);
|
|
|
|
int32_t mask1 = (1 << width) - 1;
|
|
|
|
int32_t mask2 = 1 << (width - 1);
|
|
|
|
int32_t result = (((src >> lsb) & mask1) ^ mask2) - mask2;
|
|
|
|
CHECK(test(lsb, mask1, mask2) == result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testExtractSignedBitfield64()
|
|
|
|
{
|
|
|
|
if (JSC::Options::defaultB3OptLevel() < 2)
|
|
|
|
return;
|
|
|
|
int64_t src = 0xffffffffffffffff;
|
2021-07-06 22:49:32 +00:00
|
|
|
Vector<int32_t> lsbs = { 1, 30, 62 };
|
|
|
|
Vector<int32_t> widths = { 62, 33, 1 };
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
|
|
|
|
// Test Pattern: ((src >> lsb) << amount) >> amount
|
|
|
|
// where: amount = datasize - width
|
2021-07-06 22:49:32 +00:00
|
|
|
auto test = [&] (int32_t lsb, int32_t amount) -> int64_t {
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* srcValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
|
2021-07-06 22:49:32 +00:00
|
|
|
Value* amountValue = root->appendNew<Const32Value>(proc, Origin(), amount);
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
|
|
|
|
Value* rightShiftValue = root->appendNew<Value>(proc, ZShr, Origin(), srcValue, lsbValue);
|
|
|
|
Value* leftShiftValue = root->appendNew<Value>(proc, Shl, Origin(), rightShiftValue, amountValue);
|
|
|
|
Value* signedRightShiftValue = root->appendNew<Value>(proc, SShr, Origin(), leftShiftValue, amountValue);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), signedRightShiftValue);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
if (isARM64())
|
|
|
|
checkUsesInstruction(*code, "sbfx");
|
|
|
|
return invoke<uint64_t>(*code, src);
|
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < lsbs.size(); ++i) {
|
2021-07-06 22:49:32 +00:00
|
|
|
int32_t lsb = lsbs.at(i);
|
|
|
|
int32_t width = widths.at(i);
|
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Canonical link: https://commits.webkit.org/239243@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279378 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-29 18:41:28 +00:00
|
|
|
int64_t amount = CHAR_BIT * sizeof(src) - width;
|
|
|
|
int64_t result = ((src >> lsb) << amount) >> amount;
|
|
|
|
CHECK(test(lsb, amount) == result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:01:53 +00:00
|
|
|
void testBitOrBitOrArgImmImm32(int a, int b, int c)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* innerBitOr = root->appendNew<Value>(
|
|
|
|
proc, BitOr, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitOr, Origin(),
|
|
|
|
innerBitOr,
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), c)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, a) == ((a | b) | c));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrImmBitOrArgImm32(int a, int b, int c)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* innerBitOr = root->appendNew<Value>(
|
|
|
|
proc, BitOr, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), c));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitOr, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a),
|
|
|
|
innerBitOr));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, b) == (a | (b | c)));
|
|
|
|
}
|
|
|
|
|
|
|
|
double bitOrDouble(double a, double b)
|
|
|
|
{
|
|
|
|
return bitwise_cast<double>(bitwise_cast<uint64_t>(a) | bitwise_cast<uint64_t>(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrArgDouble(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argument, argument);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), bitOrDouble(a, a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrArgsDouble(double a, double b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentA = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* argumentB = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1);
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argumentA, argumentB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a, b), bitOrDouble(a, b)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrArgImmDouble(double a, double b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentA = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* argumentB = root->appendNew<ConstDoubleValue>(proc, Origin(), b);
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argumentA, argumentB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a, b), bitOrDouble(a, b)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrImmsDouble(double a, double b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentA = root->appendNew<ConstDoubleValue>(proc, Origin(), a);
|
|
|
|
Value* argumentB = root->appendNew<ConstDoubleValue>(proc, Origin(), b);
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argumentA, argumentB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), bitOrDouble(a, b)));
|
|
|
|
}
|
|
|
|
|
|
|
|
float bitOrFloat(float a, float b)
|
|
|
|
{
|
|
|
|
return bitwise_cast<float>(bitwise_cast<uint32_t>(a) | bitwise_cast<uint32_t>(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrArgFloat(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argument, argument);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), bitOrFloat(a, a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrArgsFloat(float a, float b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentA = root->appendNew<Value>(proc, BitwiseCast, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
Value* argumentB = root->appendNew<Value>(proc, BitwiseCast, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argumentA, argumentB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b)), bitOrFloat(a, b)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrArgImmFloat(float a, float b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentA = root->appendNew<Value>(proc, BitwiseCast, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
Value* argumentB = root->appendNew<ConstFloatValue>(proc, Origin(), b);
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argumentA, argumentB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b)), bitOrFloat(a, b)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrImmsFloat(float a, float b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentA = root->appendNew<ConstFloatValue>(proc, Origin(), a);
|
|
|
|
Value* argumentB = root->appendNew<ConstFloatValue>(proc, Origin(), b);
|
|
|
|
Value* result = root->appendNew<Value>(proc, BitOr, Origin(), argumentA, argumentB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc), bitOrFloat(a, b)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitOrArgsFloatWithUselessDoubleConversion(float a, float b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentA = root->appendNew<Value>(proc, BitwiseCast, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
Value* argumentB = root->appendNew<Value>(proc, BitwiseCast, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
|
|
|
|
Value* argumentAasDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), argumentA);
|
|
|
|
Value* argumentBasDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), argumentB);
|
|
|
|
Value* doubleResult = root->appendNew<Value>(proc, BitOr, Origin(), argumentAasDouble, argumentBasDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), doubleResult);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), floatResult);
|
|
|
|
|
|
|
|
double doubleA = a;
|
|
|
|
double doubleB = b;
|
|
|
|
float expected = static_cast<float>(bitOrDouble(doubleA, doubleB));
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b)), expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorArgs(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a, b) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorSameArg(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
argument,
|
|
|
|
argument));
|
|
|
|
|
|
|
|
CHECK(!compileAndRun<int64_t>(proc, a));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorAndAndArgs(int64_t a, int64_t b, int64_t c)
|
|
|
|
{
|
|
|
|
// We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
|
|
|
|
// ((a & b) ^ (a & c))
|
|
|
|
// ((a & b) ^ (c & a))
|
|
|
|
// ((b & a) ^ (a & c))
|
|
|
|
// ((b & a) ^ (c & a))
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* argC = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
|
|
|
|
Value* andAB = i & 2 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
|
|
|
|
: root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
|
|
|
|
Value* andAC = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argC)
|
|
|
|
: root->appendNew<Value>(proc, BitAnd, Origin(), argC, argA);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
andAB,
|
|
|
|
andAC));
|
|
|
|
|
|
|
|
CHECK_EQ(compileAndRun<int64_t>(proc, a, b, c), ((a & b) ^ (a & c)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[JSC] 32bit bitwide operation with all-one (-1) is wrong in B3
https://bugs.webkit.org/show_bug.cgi?id=201634
Reviewed by Mark Lam and Robin Morisset.
This patch includes two things. One is fixing 32bit bitwise operation with allOne constants. Another is fixing the existing bug in BitAnd strength reduction.
1. 32bit bitwise operation with allOne constants
Accidentally, the B3::Value is ConstInt32(-1), `value->isInt(std::numeric_limits<uint32_t>::max())` returns `false`!
For example, in BitAnd strength reduction,
1034 // Turn this: BitAnd(value, all-ones)
1035 // Into this: value.
1036 if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
1037 || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
1038 replaceWithIdentity(m_value->child(0));
1039 break;
1040 }
We use `m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max())`. However, Value::isInt is,
262 inline bool Value::isInt(int64_t value) const
263 {
264 return hasInt() && asInt() == value;
265 }
So, UINT32_MAX is expanded to int64_t, but it is not -1 since UINT32_MAX can be representable in int64_t. And Value::asInt implementation is,
257 inline int64_t Value::asInt() const
258 {
259 return hasInt32() ? asInt32() : asInt64();
260 }
So, we perform `static_cast<int64_t>(-1) == static_cast<int64_t>(UINT32_MAX)`. This is false, but this comparison is not what we want!
We should use `isInt32` and `isInt64` for bit patterns (like, operands for Bitwise opcodes).
2. BitAnd and BitOr strength reduction bug
We also fix the following optimization.
// Turn this: BitAnd(Op(value, constant1), constant2)
// where !(constant1 & constant2)
// and Op is BitOr or BitXor
// into this: BitAnd(value, constant2)
Since we stop further optimization when we match `if (m_value->child(1)->hasInt())`, the following optimization is never taken.
// Turn this: BitAnd(BitXor(x, allOnes), c)
// Into this: BitXor(BitOr(x, ~c), allOnes)
And we also found that this not-used optimization has a bug not inserting a newly produced constant B3::Value. This patch also fixes it.
For both, this patch adds tests. And (2) fix can be ensured that the testb3 does not crash with validate-graph option.
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testBitAndNotNot32):
(testBitAndNotImm):
(testBitAndNotImm32):
(testBitOrAndAndArgs32):
(testBitOrAndSameArgs32):
(testBitOrNotNot32):
(testBitOrNotImm32):
(addBitTests):
* b3/testb3_3.cpp:
(testBitXorAndAndArgs32):
(testBitXorAndSameArgs32):
Canonical link: https://commits.webkit.org/215314@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249747 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2019-09-11 00:46:32 +00:00
|
|
|
void testBitXorAndAndArgs32(int32_t a, int32_t b, int32_t c)
|
|
|
|
{
|
|
|
|
// We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
|
|
|
|
// ((a & b) ^ (a & c))
|
|
|
|
// ((a & b) ^ (c & a))
|
|
|
|
// ((b & a) ^ (a & c))
|
|
|
|
// ((b & a) ^ (c & a))
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* argC = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2));
|
|
|
|
Value* andAB = i & 2 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
|
|
|
|
: root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
|
|
|
|
Value* andAC = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argC)
|
|
|
|
: root->appendNew<Value>(proc, BitAnd, Origin(), argC, argA);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
andAB,
|
|
|
|
andAC));
|
|
|
|
|
|
|
|
CHECK_EQ(compileAndRun<int32_t>(proc, a, b, c), ((a & b) ^ (a & c)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:01:53 +00:00
|
|
|
void testBitXorAndSameArgs(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
// We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
|
|
|
|
// ((a & b) ^ a)
|
|
|
|
// ((b & a) ^ a)
|
|
|
|
// (a ^ (a & b))
|
|
|
|
// (a ^ (b & a))
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* andAB = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
|
|
|
|
: root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
|
|
|
|
Value* result = i & 2 ? root->appendNew<Value>(proc, BitXor, Origin(), andAB, argA)
|
|
|
|
: root->appendNew<Value>(proc, BitXor, Origin(), argA, andAB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK_EQ(compileAndRun<int64_t>(proc, a, b), ((a & b) ^ a));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[JSC] 32bit bitwide operation with all-one (-1) is wrong in B3
https://bugs.webkit.org/show_bug.cgi?id=201634
Reviewed by Mark Lam and Robin Morisset.
This patch includes two things. One is fixing 32bit bitwise operation with allOne constants. Another is fixing the existing bug in BitAnd strength reduction.
1. 32bit bitwise operation with allOne constants
Accidentally, the B3::Value is ConstInt32(-1), `value->isInt(std::numeric_limits<uint32_t>::max())` returns `false`!
For example, in BitAnd strength reduction,
1034 // Turn this: BitAnd(value, all-ones)
1035 // Into this: value.
1036 if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
1037 || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
1038 replaceWithIdentity(m_value->child(0));
1039 break;
1040 }
We use `m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max())`. However, Value::isInt is,
262 inline bool Value::isInt(int64_t value) const
263 {
264 return hasInt() && asInt() == value;
265 }
So, UINT32_MAX is expanded to int64_t, but it is not -1 since UINT32_MAX can be representable in int64_t. And Value::asInt implementation is,
257 inline int64_t Value::asInt() const
258 {
259 return hasInt32() ? asInt32() : asInt64();
260 }
So, we perform `static_cast<int64_t>(-1) == static_cast<int64_t>(UINT32_MAX)`. This is false, but this comparison is not what we want!
We should use `isInt32` and `isInt64` for bit patterns (like, operands for Bitwise opcodes).
2. BitAnd and BitOr strength reduction bug
We also fix the following optimization.
// Turn this: BitAnd(Op(value, constant1), constant2)
// where !(constant1 & constant2)
// and Op is BitOr or BitXor
// into this: BitAnd(value, constant2)
Since we stop further optimization when we match `if (m_value->child(1)->hasInt())`, the following optimization is never taken.
// Turn this: BitAnd(BitXor(x, allOnes), c)
// Into this: BitXor(BitOr(x, ~c), allOnes)
And we also found that this not-used optimization has a bug not inserting a newly produced constant B3::Value. This patch also fixes it.
For both, this patch adds tests. And (2) fix can be ensured that the testb3 does not crash with validate-graph option.
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testBitAndNotNot32):
(testBitAndNotImm):
(testBitAndNotImm32):
(testBitOrAndAndArgs32):
(testBitOrAndSameArgs32):
(testBitOrNotNot32):
(testBitOrNotImm32):
(addBitTests):
* b3/testb3_3.cpp:
(testBitXorAndAndArgs32):
(testBitXorAndSameArgs32):
Canonical link: https://commits.webkit.org/215314@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249747 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2019-09-11 00:46:32 +00:00
|
|
|
void testBitXorAndSameArgs32(int32_t a, int32_t b)
|
|
|
|
{
|
|
|
|
// We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
|
|
|
|
// ((a & b) ^ a)
|
|
|
|
// ((b & a) ^ a)
|
|
|
|
// (a ^ (a & b))
|
|
|
|
// (a ^ (b & a))
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* andAB = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
|
|
|
|
: root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
|
|
|
|
Value* result = i & 2 ? root->appendNew<Value>(proc, BitXor, Origin(), andAB, argA)
|
|
|
|
: root->appendNew<Value>(proc, BitXor, Origin(), argA, andAB);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
CHECK_EQ(compileAndRun<int32_t>(proc, a, b), ((a & b) ^ a));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:01:53 +00:00
|
|
|
void testBitXorImms(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorArgImm(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorImmArg(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, b) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorBitXorArgImmImm(int64_t a, int64_t b, int64_t c)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* innerBitXor = root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), b));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
innerBitXor,
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), c)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a) == ((a ^ b) ^ c));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorImmBitXorArgImm(int64_t a, int64_t b, int64_t c)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* innerBitXor = root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), c));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), a),
|
|
|
|
innerBitXor));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, b) == (a ^ (b ^ c)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorArgs32(int a, int b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, a, b) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorSameArg32(int a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
argument,
|
|
|
|
argument));
|
|
|
|
|
|
|
|
CHECK(!compileAndRun<int>(proc, a));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorImms32(int a, int b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorArgImm32(int a, int b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, a) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorImmArg32(int a, int b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, b) == (a ^ b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorBitXorArgImmImm32(int a, int b, int c)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* innerBitXor = root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
innerBitXor,
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), c)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, a) == ((a ^ b) ^ c));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitXorImmBitXorArgImm32(int a, int b, int c)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* innerBitXor = root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), c));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a),
|
|
|
|
innerBitXor));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, b) == (a ^ (b ^ c)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitNotArg(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), -1),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc, a), static_cast<int64_t>((static_cast<uint64_t>(a) ^ 0xffffffffffffffff))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitNotImm(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), -1),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), a)));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc, a), static_cast<int64_t>((static_cast<uint64_t>(a) ^ 0xffffffffffffffff))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitNotMem(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* load = root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address);
|
|
|
|
Value* notLoad = root->appendNew<Value>(proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), -1),
|
|
|
|
load);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), notLoad, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
|
|
|
|
|
|
|
|
int64_t input = a;
|
|
|
|
compileAndRun<int32_t>(proc, &input);
|
|
|
|
CHECK(isIdentical(input, static_cast<int64_t>((static_cast<uint64_t>(a) ^ 0xffffffffffffffff))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitNotArg32(int32_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), -1),
|
|
|
|
argument));
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, a), static_cast<int32_t>((static_cast<uint32_t>(a) ^ 0xffffffff))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitNotImm32(int32_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), -1),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a)));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, a), static_cast<int32_t>((static_cast<uint32_t>(a) ^ 0xffffffff))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitNotMem32(int32_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* load = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), address);
|
|
|
|
Value* notLoad = root->appendNew<Value>(proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), -1),
|
|
|
|
load);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), notLoad, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
|
|
|
|
|
|
|
|
int32_t input = a;
|
|
|
|
compileAndRun<int32_t>(proc, &input);
|
|
|
|
CHECK(isIdentical(input, static_cast<int32_t>((static_cast<uint32_t>(a) ^ 0xffffffff))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testNotOnBooleanAndBranch32(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
|
|
|
|
Value* arg1 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* arg2 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* argsAreEqual = root->appendNew<Value>(proc, Equal, Origin(), arg1, arg2);
|
|
|
|
Value* argsAreNotEqual = root->appendNew<Value>(proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), 1),
|
|
|
|
argsAreEqual);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Branch, Origin(),
|
|
|
|
argsAreNotEqual,
|
|
|
|
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
thenCase->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
thenCase->appendNew<Const32Value>(proc, Origin(), 42));
|
|
|
|
|
|
|
|
elseCase->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
elseCase->appendNew<Const32Value>(proc, Origin(), -42));
|
|
|
|
|
|
|
|
int32_t expectedValue = (a != b) ? 42 : -42;
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a, b) == expectedValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitNotOnBooleanAndBranch32(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
|
|
|
|
Value* arg1 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* arg2 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* argsAreEqual = root->appendNew<Value>(proc, Equal, Origin(), arg1, arg2);
|
|
|
|
Value* bitNotArgsAreEqual = root->appendNew<Value>(proc, BitXor, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), -1),
|
|
|
|
argsAreEqual);
|
|
|
|
|
|
|
|
root->appendNewControlValue(proc, Branch, Origin(),
|
|
|
|
bitNotArgsAreEqual, FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
thenCase->appendNewControlValue(proc, Return, Origin(),
|
|
|
|
thenCase->appendNew<Const32Value>(proc, Origin(), 42));
|
|
|
|
|
|
|
|
elseCase->appendNewControlValue(proc, Return, Origin(),
|
|
|
|
elseCase->appendNew<Const32Value>(proc, Origin(), -42));
|
|
|
|
|
|
|
|
static constexpr int32_t expectedValue = 42;
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a, b) == expectedValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlArgs(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a, b) == (a << b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlImms(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
b = b & 0x3f; // to avoid undefined behaviour below
|
|
|
|
CHECK(compileAndRun<int64_t>(proc) == (a << b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlArgImm(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
b = b & 0x3f; // to avoid undefined behaviour below
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a) == (a << b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlSShrArgImm(int64_t a, int64_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* constB = root->appendNew<Const32Value>(proc, Origin(), b);
|
|
|
|
Value* innerShift = root->appendNew<Value>(proc, SShr, Origin(), argA, constB);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
innerShift,
|
|
|
|
constB));
|
|
|
|
|
|
|
|
b = b & 0x3f; // to avoid undefined behaviour below
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a) == ((a >> b) << b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlArg32(int32_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* value = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Shl, Origin(), value, value));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a) == (a << a));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlArgs32(int32_t a, int32_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a, b) == (a << b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlImms32(int32_t a, int32_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
b = b & 0x1f; // to avoid undefined behaviour below
|
|
|
|
CHECK(compileAndRun<int32_t>(proc) == (a << b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlArgImm32(int32_t a, int32_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
b = b & 0x1f; // to avoid undefined behaviour below
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a) == (a << b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testShlZShrArgImm32(int32_t a, int32_t b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argA = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* constB = root->appendNew<Const32Value>(proc, Origin(), b);
|
|
|
|
Value* innerShift = root->appendNew<Value>(proc, ZShr, Origin(), argA, constB);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Shl, Origin(),
|
|
|
|
innerShift,
|
|
|
|
constB));
|
|
|
|
|
|
|
|
b = b & 0x1f; // to avoid undefined behaviour below
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a) == static_cast<int32_t>((static_cast<uint32_t>(a) >> b) << b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testSShrArgs(int64_t a, int64_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, SShr, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a, b) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testSShrImms(int64_t a, int64_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, SShr, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testSShrArgImm(int64_t a, int64_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, SShr, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, a) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testSShrArg32(int32_t a)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* value = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, SShr, Origin(), value, value));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a) == (a >> (a & 31)));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testSShrArgs32(int32_t a, int32_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, SShr, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a, b) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testSShrImms32(int32_t a, int32_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, SShr, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int32_t>(proc) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testSShrArgImm32(int32_t a, int32_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, SShr, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, a) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testZShrArgs(uint64_t a, uint64_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, ZShr, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<uint64_t>(proc, a, b) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testZShrImms(uint64_t a, uint64_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, ZShr, Origin(),
|
|
|
|
root->appendNew<Const64Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<uint64_t>(proc) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testZShrArgImm(uint64_t a, uint64_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, ZShr, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<uint64_t>(proc, a) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testZShrArg32(uint32_t a)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* value = root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, ZShr, Origin(), value, value));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<uint32_t>(proc, a) == (a >> (a & 31)));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testZShrArgs32(uint32_t a, uint32_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, ZShr, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<uint32_t>(proc, a, b) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testZShrImms32(uint32_t a, uint32_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, ZShr, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), a),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<uint32_t>(proc) == (a >> b));
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
static void testZShrArgImm32(uint32_t a, uint32_t b)
|
2019-08-01 01:01:53 +00:00
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, ZShr, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), b)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<uint32_t>(proc, a) == (a >> b));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename IntegerType>
|
|
|
|
static unsigned countLeadingZero(IntegerType value)
|
|
|
|
{
|
|
|
|
unsigned bitCount = sizeof(IntegerType) * 8;
|
|
|
|
if (!value)
|
|
|
|
return bitCount;
|
|
|
|
|
|
|
|
unsigned counter = 0;
|
|
|
|
while (!(static_cast<uint64_t>(value) & (1l << (bitCount - 1)))) {
|
|
|
|
value <<= 1;
|
|
|
|
++counter;
|
|
|
|
}
|
|
|
|
return counter;
|
|
|
|
}
|
|
|
|
|
|
|
|
void testClzArg64(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* clzValue = root->appendNew<Value>(proc, Clz, Origin(), argument);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), clzValue);
|
|
|
|
CHECK(compileAndRun<unsigned>(proc, a) == countLeadingZero(a));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testClzMem64(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* value = root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address);
|
|
|
|
Value* clzValue = root->appendNew<Value>(proc, Clz, Origin(), value);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), clzValue);
|
|
|
|
CHECK(compileAndRun<unsigned>(proc, &a) == countLeadingZero(a));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testClzArg32(int32_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* clzValue = root->appendNew<Value>(proc, Clz, Origin(), argument);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), clzValue);
|
|
|
|
CHECK(compileAndRun<unsigned>(proc, a) == countLeadingZero(a));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testClzMem32(int32_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* value = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), address);
|
|
|
|
Value* clzValue = root->appendNew<Value>(proc, Clz, Origin(), value);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), clzValue);
|
|
|
|
CHECK(compileAndRun<unsigned>(proc, &a) == countLeadingZero(a));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Abs, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0)));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), fabs(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsImm(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstDoubleValue>(proc, Origin(), a);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Abs, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), fabs(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsMem(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Abs, Origin(), loadDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &a), fabs(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsAbsArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* firstAbs = root->appendNew<Value>(proc, Abs, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
|
|
|
|
Value* secondAbs = root->appendNew<Value>(proc, Abs, Origin(), firstAbs);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), secondAbs);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), fabs(fabs(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsNegArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* neg = root->appendNew<Value>(proc, Neg, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
|
|
|
|
Value* abs = root->appendNew<Value>(proc, Abs, Origin(), neg);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), abs);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), fabs(- a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsBitwiseCastArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentAsInt64 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argumentAsDouble = root->appendNew<Value>(proc, BitwiseCast, Origin(), argumentAsInt64);
|
|
|
|
Value* absValue = root->appendNew<Value>(proc, Abs, Origin(), argumentAsDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), absValue);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, bitwise_cast<int64_t>(a)), fabs(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastAbsBitwiseCastArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentAsInt64 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argumentAsDouble = root->appendNew<Value>(proc, BitwiseCast, Origin(), argumentAsInt64);
|
|
|
|
Value* absValue = root->appendNew<Value>(proc, Abs, Origin(), argumentAsDouble);
|
|
|
|
Value* resultAsInt64 = root->appendNew<Value>(proc, BitwiseCast, Origin(), absValue);
|
|
|
|
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), resultAsInt64);
|
|
|
|
|
|
|
|
int64_t expectedResult = bitwise_cast<int64_t>(fabs(a));
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc, bitwise_cast<int64_t>(a)), expectedResult));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Abs, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(static_cast<float>(fabs(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsImm(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstFloatValue>(proc, Origin(), a);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Abs, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(static_cast<float>(fabs(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsMem(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), address);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Abs, Origin(), loadFloat);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, &a), bitwise_cast<int32_t>(static_cast<float>(fabs(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsAbsArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* firstAbs = root->appendNew<Value>(proc, Abs, Origin(), argument);
|
|
|
|
Value* secondAbs = root->appendNew<Value>(proc, Abs, Origin(), firstAbs);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), secondAbs);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), static_cast<float>(fabs(fabs(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsNegArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* neg = root->appendNew<Value>(proc, Neg, Origin(), argument);
|
|
|
|
Value* abs = root->appendNew<Value>(proc, Abs, Origin(), neg);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), abs);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), static_cast<float>(fabs(- a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsBitwiseCastArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentAsInt32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argumentAsfloat = root->appendNew<Value>(proc, BitwiseCast, Origin(), argumentAsInt32);
|
|
|
|
Value* absValue = root->appendNew<Value>(proc, Abs, Origin(), argumentAsfloat);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), absValue);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), static_cast<float>(fabs(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastAbsBitwiseCastArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argumentAsInt32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argumentAsfloat = root->appendNew<Value>(proc, BitwiseCast, Origin(), argumentAsInt32);
|
|
|
|
Value* absValue = root->appendNew<Value>(proc, Abs, Origin(), argumentAsfloat);
|
|
|
|
Value* resultAsInt64 = root->appendNew<Value>(proc, BitwiseCast, Origin(), absValue);
|
|
|
|
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), resultAsInt64);
|
|
|
|
|
|
|
|
int32_t expectedResult = bitwise_cast<int32_t>(static_cast<float>(fabs(a)));
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), expectedResult));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsArgWithUselessDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Abs, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(static_cast<float>(fabs(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAbsArgWithEffectfulDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Abs, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
Value* doubleAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), result, doubleAddress);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
double effect = 0;
|
|
|
|
int32_t resultValue = compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a), &effect);
|
|
|
|
CHECK(isIdentical(resultValue, bitwise_cast<int32_t>(static_cast<float>(fabs(a)))));
|
|
|
|
CHECK(isIdentical(effect, static_cast<double>(fabs(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Ceil, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0)));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), ceil(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilImm(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstDoubleValue>(proc, Origin(), a);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Ceil, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), ceil(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilMem(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Ceil, Origin(), loadDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &a), ceil(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilCeilArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* firstCeil = root->appendNew<Value>(proc, Ceil, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
|
|
|
|
Value* secondCeil = root->appendNew<Value>(proc, Ceil, Origin(), firstCeil);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), secondCeil);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), ceil(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorCeilArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* firstCeil = root->appendNew<Value>(proc, Ceil, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
|
|
|
|
Value* wrappingFloor = root->appendNew<Value>(proc, Floor, Origin(), firstCeil);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), wrappingFloor);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), ceil(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilIToD64(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argumentAsDouble = root->appendNew<Value>(proc, IToD, Origin(), argument);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Ceil, Origin(), argumentAsDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), ceil(static_cast<double>(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilIToD32(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argumentAsDouble = root->appendNew<Value>(proc, IToD, Origin(), argument);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Ceil, Origin(), argumentAsDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), ceil(static_cast<double>(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Ceil, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(ceilf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilImm(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstFloatValue>(proc, Origin(), a);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Ceil, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(ceilf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilMem(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), address);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Ceil, Origin(), loadFloat);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, &a), bitwise_cast<int32_t>(ceilf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilCeilArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* firstCeil = root->appendNew<Value>(proc, Ceil, Origin(), argument);
|
|
|
|
Value* secondCeil = root->appendNew<Value>(proc, Ceil, Origin(), firstCeil);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), secondCeil);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), ceilf(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorCeilArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* firstCeil = root->appendNew<Value>(proc, Ceil, Origin(), argument);
|
|
|
|
Value* wrappingFloor = root->appendNew<Value>(proc, Floor, Origin(), firstCeil);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), wrappingFloor);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), ceilf(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilArgWithUselessDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Ceil, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(ceilf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilArgWithEffectfulDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Ceil, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
Value* doubleAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), result, doubleAddress);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
double effect = 0;
|
|
|
|
int32_t resultValue = compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a), &effect);
|
|
|
|
CHECK(isIdentical(resultValue, bitwise_cast<int32_t>(ceilf(a))));
|
|
|
|
CHECK(isIdentical(effect, static_cast<double>(ceilf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Floor, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0)));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), floor(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorImm(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstDoubleValue>(proc, Origin(), a);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Floor, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), floor(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorMem(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Floor, Origin(), loadDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &a), floor(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorFloorArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
|
|
|
|
Value* secondFloor = root->appendNew<Value>(proc, Floor, Origin(), firstFloor);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), secondFloor);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), floor(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilFloorArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
|
|
|
|
Value* wrappingCeil = root->appendNew<Value>(proc, Ceil, Origin(), firstFloor);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), wrappingCeil);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), floor(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorIToD64(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argumentAsDouble = root->appendNew<Value>(proc, IToD, Origin(), argument);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Floor, Origin(), argumentAsDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), floor(static_cast<double>(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorIToD32(int64_t a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argumentAsDouble = root->appendNew<Value>(proc, IToD, Origin(), argument);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Floor, Origin(), argumentAsDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), floor(static_cast<double>(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Floor, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(floorf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorImm(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstFloatValue>(proc, Origin(), a);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Floor, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(floorf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorMem(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), address);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Floor, Origin(), loadFloat);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, &a), bitwise_cast<int32_t>(floorf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorFloorArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(), argument);
|
|
|
|
Value* secondFloor = root->appendNew<Value>(proc, Floor, Origin(), firstFloor);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), secondFloor);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), floorf(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCeilFloorArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(), argument);
|
|
|
|
Value* wrappingCeil = root->appendNew<Value>(proc, Ceil, Origin(), firstFloor);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), wrappingCeil);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), floorf(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorArgWithUselessDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Floor, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(floorf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloorArgWithEffectfulDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Floor, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
Value* doubleAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), result, doubleAddress);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
double effect = 0;
|
|
|
|
int32_t resultValue = compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a), &effect);
|
|
|
|
CHECK(isIdentical(resultValue, bitwise_cast<int32_t>(floorf(a))));
|
|
|
|
CHECK(isIdentical(effect, static_cast<double>(floorf(a))));
|
|
|
|
}
|
|
|
|
|
|
|
|
double correctSqrt(double value)
|
|
|
|
{
|
|
|
|
#if CPU(X86) || CPU(X86_64)
|
|
|
|
double result;
|
|
|
|
asm ("sqrtsd %1, %0" : "=x"(result) : "x"(value));
|
|
|
|
return result;
|
|
|
|
#else
|
|
|
|
return sqrt(value);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtArg(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Sqrt, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0)));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, a), correctSqrt(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtImm(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstDoubleValue>(proc, Origin(), a);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Sqrt, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), correctSqrt(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtMem(double a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(proc, Sqrt, Origin(), loadDouble));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &a), correctSqrt(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtArg(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Sqrt, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(static_cast<float>(correctSqrt(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtImm(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstFloatValue>(proc, Origin(), a);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Sqrt, Origin(), argument);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(static_cast<float>(correctSqrt(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtMem(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), address);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Sqrt, Origin(), loadFloat);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, &a), bitwise_cast<int32_t>(static_cast<float>(correctSqrt(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtArgWithUselessDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Sqrt, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(static_cast<float>(correctSqrt(a)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSqrtArgWithEffectfulDoubleConversion(float a)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* result = root->appendNew<Value>(proc, Sqrt, Origin(), asDouble);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
|
|
|
|
Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
|
|
|
|
Value* doubleAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), result, doubleAddress);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), result32);
|
|
|
|
|
|
|
|
double effect = 0;
|
|
|
|
int32_t resultValue = compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a), &effect);
|
|
|
|
CHECK(isIdentical(resultValue, bitwise_cast<int32_t>(static_cast<float>(correctSqrt(a)))));
|
|
|
|
double expected = static_cast<double>(correctSqrt(a));
|
|
|
|
CHECK(isIdentical(effect, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCompareTwoFloatToDouble(float a, float b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* arg1As32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* arg1Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg1As32);
|
|
|
|
Value* arg1AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg1Float);
|
|
|
|
|
|
|
|
Value* arg2As32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* arg2Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg2As32);
|
|
|
|
Value* arg2AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg2Float);
|
|
|
|
Value* equal = root->appendNew<Value>(proc, Equal, Origin(), arg1AsDouble, arg2AsDouble);
|
|
|
|
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), equal);
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b)) == (a == b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCompareOneFloatToDouble(float a, double b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* arg1As32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* arg1Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg1As32);
|
|
|
|
Value* arg1AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg1Float);
|
|
|
|
|
|
|
|
Value* arg2AsDouble = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* equal = root->appendNew<Value>(proc, Equal, Origin(), arg1AsDouble, arg2AsDouble);
|
|
|
|
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), equal);
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, bitwise_cast<int32_t>(a), b) == (a == b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testCompareFloatToDoubleThroughPhi(float a, float b)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
BasicBlock* tail = proc.addBlock();
|
|
|
|
|
|
|
|
Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
|
|
|
|
Value* arg1As32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* arg1Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg1As32);
|
|
|
|
Value* arg1AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg1Float);
|
|
|
|
|
|
|
|
Value* arg2AsDouble = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* arg2AsFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), arg2AsDouble);
|
|
|
|
Value* arg2AsFRoundedDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg2AsFloat);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Branch, Origin(),
|
|
|
|
condition,
|
|
|
|
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), arg1AsDouble);
|
|
|
|
thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* elseConst = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), 0.);
|
|
|
|
UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), elseConst);
|
|
|
|
elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
|
|
|
|
thenValue->setPhi(doubleInput);
|
|
|
|
elseValue->setPhi(doubleInput);
|
|
|
|
Value* equal = tail->appendNew<Value>(proc, Equal, Origin(), doubleInput, arg2AsFRoundedDouble);
|
|
|
|
tail->appendNewControlValue(proc, Return, Origin(), equal);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
int32_t integerA = bitwise_cast<int32_t>(a);
|
|
|
|
double doubleB = b;
|
|
|
|
CHECK(invoke<int64_t>(*code, 1, integerA, doubleB) == (a == b));
|
|
|
|
CHECK(invoke<int64_t>(*code, 0, integerA, doubleB) == (b == 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testDoubleToFloatThroughPhi(float value)
|
|
|
|
{
|
|
|
|
// Simple case of:
|
|
|
|
// if (a) {
|
|
|
|
// x = DoubleAdd(a, b)
|
|
|
|
// else
|
|
|
|
// x = DoubleAdd(a, c)
|
|
|
|
// DoubleToFloat(x)
|
|
|
|
//
|
|
|
|
// Both Adds can be converted to float add.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
BasicBlock* tail = proc.addBlock();
|
|
|
|
|
|
|
|
Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* argAsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Branch, Origin(),
|
|
|
|
condition,
|
|
|
|
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
Value* postitiveConst = thenCase->appendNew<ConstDoubleValue>(proc, Origin(), 42.5f);
|
|
|
|
Value* thenAdd = thenCase->appendNew<Value>(proc, Add, Origin(), argAsDouble, postitiveConst);
|
|
|
|
UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), thenAdd);
|
|
|
|
thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* elseConst = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), M_PI);
|
|
|
|
UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), elseConst);
|
|
|
|
elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
|
|
|
|
thenValue->setPhi(doubleInput);
|
|
|
|
elseValue->setPhi(doubleInput);
|
|
|
|
Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), doubleInput);
|
|
|
|
tail->appendNewControlValue(proc, Return, Origin(), floatResult);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 1, bitwise_cast<int32_t>(value)), value + 42.5f));
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 0, bitwise_cast<int32_t>(value)), static_cast<float>(M_PI)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testReduceFloatToDoubleValidates()
|
|
|
|
{
|
|
|
|
// Simple case of:
|
|
|
|
// f = DoubleToFloat(Bitcast(argGPR0))
|
|
|
|
// if (a) {
|
|
|
|
// x = FloatConst()
|
|
|
|
// else
|
|
|
|
// x = FloatConst()
|
|
|
|
// p = Phi(x)
|
|
|
|
// a = Mul(p, p)
|
|
|
|
// b = Add(a, f)
|
|
|
|
// c = Add(p, b)
|
|
|
|
// Return(c)
|
|
|
|
//
|
|
|
|
// This should not crash in the validator after ReduceFloatToDouble.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
BasicBlock* tail = proc.addBlock();
|
|
|
|
|
|
|
|
Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* thingy = root->appendNew<Value>(proc, BitwiseCast, Origin(), condition);
|
|
|
|
thingy = root->appendNew<Value>(proc, DoubleToFloat, Origin(), thingy); // Make the phase think it has work to do.
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Branch, Origin(),
|
|
|
|
condition,
|
|
|
|
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(),
|
|
|
|
thenCase->appendNew<ConstFloatValue>(proc, Origin(), 11.5));
|
|
|
|
thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(),
|
|
|
|
elseCase->appendNew<ConstFloatValue>(proc, Origin(), 10.5));
|
|
|
|
elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* phi = tail->appendNew<Value>(proc, Phi, Float, Origin());
|
|
|
|
thenValue->setPhi(phi);
|
|
|
|
elseValue->setPhi(phi);
|
|
|
|
Value* result = tail->appendNew<Value>(proc, Mul, Origin(),
|
|
|
|
phi, phi);
|
|
|
|
result = tail->appendNew<Value>(proc, Add, Origin(),
|
|
|
|
result,
|
|
|
|
thingy);
|
|
|
|
result = tail->appendNew<Value>(proc, Add, Origin(),
|
|
|
|
phi,
|
|
|
|
result);
|
|
|
|
tail->appendNewControlValue(proc, Return, Origin(), result);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 1), 11.5f * 11.5f + static_cast<float>(bitwise_cast<double>(static_cast<uint64_t>(1))) + 11.5f));
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 0), 10.5f * 10.5f + static_cast<float>(bitwise_cast<double>(static_cast<uint64_t>(0))) + 10.5f));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testDoubleProducerPhiToFloatConversion(float value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
BasicBlock* tail = proc.addBlock();
|
|
|
|
|
|
|
|
Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Branch, Origin(),
|
|
|
|
condition,
|
|
|
|
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
Value* asDouble = thenCase->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), asDouble);
|
|
|
|
thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* constDouble = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), 42.5);
|
|
|
|
UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), constDouble);
|
|
|
|
elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
|
|
|
|
thenValue->setPhi(doubleInput);
|
|
|
|
elseValue->setPhi(doubleInput);
|
|
|
|
|
|
|
|
Value* argAsDoubleAgain = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* finalAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, argAsDoubleAgain);
|
|
|
|
Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), finalAdd);
|
|
|
|
tail->appendNewControlValue(proc, Return, Origin(), floatResult);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 1, bitwise_cast<int32_t>(value)), value + value));
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 0, bitwise_cast<int32_t>(value)), 42.5f + value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testDoubleProducerPhiToFloatConversionWithDoubleConsumer(float value)
|
|
|
|
{
|
|
|
|
// In this case, the Upsilon-Phi effectively contains a Float value, but it is used
|
|
|
|
// as a Float and as a Double.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
BasicBlock* tail = proc.addBlock();
|
|
|
|
|
|
|
|
Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Branch, Origin(),
|
|
|
|
condition,
|
|
|
|
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
Value* asDouble = thenCase->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), asDouble);
|
|
|
|
thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* constDouble = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), 42.5);
|
|
|
|
UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), constDouble);
|
|
|
|
elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
|
|
|
|
thenValue->setPhi(doubleInput);
|
|
|
|
elseValue->setPhi(doubleInput);
|
|
|
|
|
|
|
|
Value* argAsDoubleAgain = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* floatAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, argAsDoubleAgain);
|
|
|
|
|
|
|
|
// FRound.
|
|
|
|
Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), floatAdd);
|
|
|
|
Value* doubleResult = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatResult);
|
|
|
|
|
|
|
|
// This one *cannot* be eliminated
|
|
|
|
Value* doubleAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, doubleResult);
|
|
|
|
|
|
|
|
tail->appendNewControlValue(proc, Return, Origin(), doubleAdd);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
CHECK(isIdentical(invoke<double>(*code, 1, bitwise_cast<int32_t>(value)), (value + value) + static_cast<double>(value)));
|
|
|
|
CHECK(isIdentical(invoke<double>(*code, 0, bitwise_cast<int32_t>(value)), static_cast<double>((42.5f + value) + 42.5f)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testDoubleProducerPhiWithNonFloatConst(float value, double constValue)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
BasicBlock* thenCase = proc.addBlock();
|
|
|
|
BasicBlock* elseCase = proc.addBlock();
|
|
|
|
BasicBlock* tail = proc.addBlock();
|
|
|
|
|
|
|
|
Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Branch, Origin(),
|
|
|
|
condition,
|
|
|
|
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
|
|
|
|
|
|
|
|
Value* asDouble = thenCase->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), asDouble);
|
|
|
|
thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* constDouble = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), constValue);
|
|
|
|
UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), constDouble);
|
|
|
|
elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
|
|
|
|
|
|
|
|
Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
|
|
|
|
thenValue->setPhi(doubleInput);
|
|
|
|
elseValue->setPhi(doubleInput);
|
|
|
|
|
|
|
|
Value* argAsDoubleAgain = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
Value* finalAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, argAsDoubleAgain);
|
|
|
|
Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), finalAdd);
|
|
|
|
tail->appendNewControlValue(proc, Return, Origin(), floatResult);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 1, bitwise_cast<int32_t>(value)), value + value));
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, 0, bitwise_cast<int32_t>(value)), static_cast<float>(constValue + value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testDoubleArgToInt64BitwiseCast(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitwiseCast, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc, value), bitwise_cast<int64_t>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testDoubleImmToInt64BitwiseCast(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstDoubleValue>(proc, Origin(), value);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitwiseCast, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc), bitwise_cast<int64_t>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testTwoBitwiseCastOnDouble(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* first = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument);
|
|
|
|
Value* second = root->appendNew<Value>(proc, BitwiseCast, Origin(), first);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), second);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, value), value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastOnDoubleInMemory(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
Value* cast = root->appendNew<Value>(proc, BitwiseCast, Origin(), loadDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), cast);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc, &value), bitwise_cast<int64_t>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastOnDoubleInMemoryIndexed(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* scaledOffset = root->appendNew<Value>(proc, Shl, Origin(),
|
|
|
|
offset,
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), 3));
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), base, scaledOffset);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
Value* cast = root->appendNew<Value>(proc, BitwiseCast, Origin(), loadDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), cast);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc, &value, 0), bitwise_cast<int64_t>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testInt64BArgToDoubleBitwiseCast(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitwiseCast, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, value), bitwise_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testInt64BImmToDoubleBitwiseCast(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Const64Value>(proc, Origin(), value);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitwiseCast, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), bitwise_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testTwoBitwiseCastOnInt64(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* first = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument);
|
|
|
|
Value* second = root->appendNew<Value>(proc, BitwiseCast, Origin(), first);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), second);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int64_t>(proc, value), value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastOnInt64InMemory(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address);
|
|
|
|
Value* cast = root->appendNew<Value>(proc, BitwiseCast, Origin(), loadDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), cast);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &value), bitwise_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastOnInt64InMemoryIndexed(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* scaledOffset = root->appendNew<Value>(proc, Shl, Origin(),
|
|
|
|
offset,
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), 3));
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), base, scaledOffset);
|
|
|
|
MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address);
|
|
|
|
Value* cast = root->appendNew<Value>(proc, BitwiseCast, Origin(), loadDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), cast);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &value, 0), bitwise_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFloatImmToInt32BitwiseCast(float value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstFloatValue>(proc, Origin(), value);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitwiseCast, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc), bitwise_cast<int32_t>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastOnFloatInMemory(float value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), address);
|
|
|
|
Value* cast = root->appendNew<Value>(proc, BitwiseCast, Origin(), loadFloat);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), cast);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, &value), bitwise_cast<int32_t>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testInt32BArgToFloatBitwiseCast(int32_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitwiseCast, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, value), bitwise_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testInt32BImmToFloatBitwiseCast(int32_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<Const64Value>(proc, Origin(), value);
|
|
|
|
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, BitwiseCast, Origin(), argument));
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc), bitwise_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testTwoBitwiseCastOnInt32(int32_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* first = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument);
|
|
|
|
Value* second = root->appendNew<Value>(proc, BitwiseCast, Origin(), first);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), second);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<int32_t>(proc, value), value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testBitwiseCastOnInt32InMemory(int32_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadFloat = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), address);
|
|
|
|
Value* cast = root->appendNew<Value>(proc, BitwiseCast, Origin(), loadFloat);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), cast);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, &value), bitwise_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConvertDoubleToFloatArg(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asFloat);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, value), static_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConvertDoubleToFloatImm(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstDoubleValue>(proc, Origin(), value);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asFloat);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc), static_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConvertDoubleToFloatMem(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadedDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), loadedDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asFloat);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, &value), static_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConvertFloatToDoubleArg(float value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asDouble);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, bitwise_cast<int32_t>(value)), static_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConvertFloatToDoubleImm(float value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ConstFloatValue>(proc, Origin(), value);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), argument);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asDouble);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), static_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConvertFloatToDoubleMem(float value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadedFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), address);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), loadedFloat);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asDouble);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &value), static_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
2021-04-10 19:00:03 +00:00
|
|
|
void testConvertDoubleToFloatToDouble(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), asFloat);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asDouble);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, value), static_cast<double>(static_cast<float>(value))));
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:01:53 +00:00
|
|
|
void testConvertDoubleToFloatToDoubleToFloat(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), asFloat);
|
|
|
|
Value* asFloatAgain = root->appendNew<Value>(proc, DoubleToFloat, Origin(), asDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asFloatAgain);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc, value), static_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
2021-04-10 19:00:03 +00:00
|
|
|
void testConvertDoubleToFloatEqual(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), asFloat);
|
|
|
|
Value* constant = root->appendNew<ConstDoubleValue>(proc, Origin(), value);
|
|
|
|
Value* argsAreEqual = root->appendNew<Value>(proc, Equal, Origin(), asDouble, constant);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), argsAreEqual);
|
|
|
|
|
|
|
|
CHECK(compileAndRun<bool>(proc, value) == (static_cast<double>(static_cast<float>(value)) == value));
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:01:53 +00:00
|
|
|
void testLoadFloatConvertDoubleConvertFloatStoreFloat(float value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* dst = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
MemoryValue* loadedFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), src);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), loadedFloat);
|
|
|
|
Value* asFloatAgain = root->appendNew<Value>(proc, DoubleToFloat, Origin(), asDouble);
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), asFloatAgain, dst);
|
|
|
|
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
|
|
|
|
|
|
|
|
float input = value;
|
|
|
|
float output = 0.;
|
|
|
|
CHECK(!compileAndRun<int64_t>(proc, &input, &output));
|
|
|
|
CHECK(isIdentical(input, output));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFroundArg(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), asFloat);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asDouble);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, value), static_cast<double>(static_cast<float>(value))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFroundMem(double value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadedDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
|
|
|
|
Value* asFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), loadedDouble);
|
|
|
|
Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), asFloat);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), asDouble);
|
|
|
|
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc, &value), static_cast<double>(static_cast<float>(value))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToD64Arg()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* srcAsDouble = root->appendNew<Value>(proc, IToD, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsDouble);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
for (auto testValue : int64Operands())
|
|
|
|
CHECK(isIdentical(invoke<double>(*code, testValue.value), static_cast<double>(testValue.value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToF64Arg()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* srcAsFloat = root->appendNew<Value>(proc, IToF, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloat);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
for (auto testValue : int64Operands())
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, testValue.value), static_cast<float>(testValue.value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToD32Arg()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* srcAsDouble = root->appendNew<Value>(proc, IToD, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsDouble);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
for (auto testValue : int32Operands())
|
|
|
|
CHECK(isIdentical(invoke<double>(*code, testValue.value), static_cast<double>(testValue.value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToF32Arg()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* srcAsFloat = root->appendNew<Value>(proc, IToF, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloat);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
for (auto testValue : int32Operands())
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, testValue.value), static_cast<float>(testValue.value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToD64Mem()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadedSrc = root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address);
|
|
|
|
Value* srcAsDouble = root->appendNew<Value>(proc, IToD, Origin(), loadedSrc);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsDouble);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
int64_t inMemoryValue;
|
|
|
|
for (auto testValue : int64Operands()) {
|
|
|
|
inMemoryValue = testValue.value;
|
|
|
|
CHECK(isIdentical(invoke<double>(*code, &inMemoryValue), static_cast<double>(testValue.value)));
|
|
|
|
CHECK(inMemoryValue == testValue.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToF64Mem()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadedSrc = root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address);
|
|
|
|
Value* srcAsFloat = root->appendNew<Value>(proc, IToF, Origin(), loadedSrc);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloat);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
int64_t inMemoryValue;
|
|
|
|
for (auto testValue : int64Operands()) {
|
|
|
|
inMemoryValue = testValue.value;
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, &inMemoryValue), static_cast<float>(testValue.value)));
|
|
|
|
CHECK(inMemoryValue == testValue.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToD32Mem()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadedSrc = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), address);
|
|
|
|
Value* srcAsDouble = root->appendNew<Value>(proc, IToD, Origin(), loadedSrc);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsDouble);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
int32_t inMemoryValue;
|
|
|
|
for (auto testValue : int32Operands()) {
|
|
|
|
inMemoryValue = testValue.value;
|
|
|
|
CHECK(isIdentical(invoke<double>(*code, &inMemoryValue), static_cast<double>(testValue.value)));
|
|
|
|
CHECK(inMemoryValue == testValue.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToF32Mem()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
MemoryValue* loadedSrc = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), address);
|
|
|
|
Value* srcAsFloat = root->appendNew<Value>(proc, IToF, Origin(), loadedSrc);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloat);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
int32_t inMemoryValue;
|
|
|
|
for (auto testValue : int32Operands()) {
|
|
|
|
inMemoryValue = testValue.value;
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, &inMemoryValue), static_cast<float>(testValue.value)));
|
|
|
|
CHECK(inMemoryValue == testValue.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToD64Imm(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<Const64Value>(proc, Origin(), value);
|
|
|
|
Value* srcAsFloatingPoint = root->appendNew<Value>(proc, IToD, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloatingPoint);
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), static_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToF64Imm(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<Const64Value>(proc, Origin(), value);
|
|
|
|
Value* srcAsFloatingPoint = root->appendNew<Value>(proc, IToF, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloatingPoint);
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc), static_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToD32Imm(int32_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<Const32Value>(proc, Origin(), value);
|
|
|
|
Value* srcAsFloatingPoint = root->appendNew<Value>(proc, IToD, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloatingPoint);
|
|
|
|
CHECK(isIdentical(compileAndRun<double>(proc), static_cast<double>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToF32Imm(int32_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<Const32Value>(proc, Origin(), value);
|
|
|
|
Value* srcAsFloatingPoint = root->appendNew<Value>(proc, IToF, Origin(), src);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), srcAsFloatingPoint);
|
|
|
|
CHECK(isIdentical(compileAndRun<float>(proc), static_cast<float>(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToDReducedToIToF64Arg()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* srcAsDouble = root->appendNew<Value>(proc, IToD, Origin(), src);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), srcAsDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), floatResult);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
for (auto testValue : int64Operands())
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, testValue.value), static_cast<float>(testValue.value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testIToDReducedToIToF32Arg()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
Value* src = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* srcAsDouble = root->appendNew<Value>(proc, IToD, Origin(), src);
|
|
|
|
Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), srcAsDouble);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), floatResult);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
for (auto testValue : int32Operands())
|
|
|
|
CHECK(isIdentical(invoke<float>(*code, testValue.value), static_cast<float>(testValue.value)));
|
|
|
|
}
|
|
|
|
|
Add a new Air::Arg kind ZeroReg to let AIR recognise the new instructions/forms accepting zero register in ARM64
https://bugs.webkit.org/show_bug.cgi?id=227510
Reviewed by Saam Barati.
B3 is designed to be portable to many kinds of CPUs. However, to effectively
compile code to different CPUs, the compiler must eventually make explicit
instruction set details. Then, Air is introduced, and it is designed to target
individual CPU architectures and generate instructions specific to those CPUs.
Previously, Air don't recognize the zero register. This problem has been pointed
out in #174821, which was trying to introduce the new opcodes to handle the zero
register.
To solve this problem in a modular reasoning approach, a new Air operand ZeroReg
should be introduced. Its goal is to closely match the CPU instructions
accepting the zero register in ARM64. Another reason is that the new overloads
of the instructions taking the zero register can benefit instruction selection
with this implementation.
Here, the ZeroReg is added as a new kind for Air::Arg, which acts as a "high
level" operand to be emitted with the associative opcodes. In ARM64, the ZeroReg
would be emitted as a zero register.
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::storeZero64): Deleted.
(JSC::MacroAssemblerARM64::storeZero32): Deleted.
(JSC::MacroAssemblerARM64::storeZero16): Deleted.
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::storeZero32): Deleted.
(JSC::MacroAssemblerX86Common::storeZero16): Deleted.
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::storeZero64): Deleted.
* b3/B3LowerToAir.cpp:
* b3/air/AirArg.cpp:
(JSC::B3::Air::Arg::jsHash const):
(JSC::B3::Air::Arg::dump const):
(WTF::printInternal):
* b3/air/AirArg.h:
(JSC::B3::Air::Arg::zeroReg):
(JSC::B3::Air::Arg::isZeroReg const):
(JSC::B3::Air::Arg::isGP const):
(JSC::B3::Air::Arg::isFP const):
(JSC::B3::Air::Arg::isValidForm const):
(JSC::B3::Air::Arg::asZeroReg const):
* b3/air/AirLowerStackArgs.cpp:
(JSC::B3::Air::lowerStackArgs):
* b3/air/AirOpcode.opcodes:
* b3/air/opcode_generator.rb:
* b3/testb3.h:
* b3/testb3_1.cpp:
(run):
* b3/testb3_3.cpp:
(testStoreZeroReg):
Canonical link: https://commits.webkit.org/239640@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279889 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-07-13 20:38:20 +00:00
|
|
|
void testStoreZeroReg()
|
|
|
|
{
|
|
|
|
// Direct addressing
|
|
|
|
{
|
|
|
|
int32_t slot32 = 0xbaadbeef;
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const32Value>(proc, Origin(), 0);
|
|
|
|
root->appendNew<MemoryValue>(
|
|
|
|
proc, Store, Origin(), value,
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), &slot32), 0);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
invoke<int>(*code);
|
|
|
|
CHECK_EQ(slot32, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
int64_t slot64 = 0xbaadbeef;
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const64Value>(proc, Origin(), 0);
|
|
|
|
root->appendNew<MemoryValue>(
|
|
|
|
proc, Store, Origin(), value,
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), &slot64), 0);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
auto code = compileProc(proc);
|
|
|
|
invoke<int>(*code);
|
|
|
|
CHECK_EQ(slot64, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Indexed addressing
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const32Value>(proc, Origin(), 0);
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* displacement = root->appendNew<Const64Value>(proc, Origin(), -1);
|
|
|
|
|
|
|
|
Value* baseDisplacement = root->appendNew<Value>(proc, Add, Origin(), displacement, base);
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), baseDisplacement, offset);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), value, address, 0);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int32_t slot32 = 0xbaadbeef;
|
|
|
|
compileAndRun<int32_t>(proc, &slot32, 1);
|
|
|
|
CHECK_EQ(slot32, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const64Value>(proc, Origin(), 0);
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* displacement = root->appendNew<Const64Value>(proc, Origin(), -1);
|
|
|
|
|
|
|
|
Value* baseDisplacement = root->appendNew<Value>(proc, Add, Origin(), displacement, base);
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), baseDisplacement, offset);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store, Origin(), value, address, 0);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int64_t slot64 = 0xbaadbeef;
|
|
|
|
compileAndRun<int64_t>(proc, &slot64, 1);
|
|
|
|
CHECK_EQ(slot64, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:01:53 +00:00
|
|
|
void testStore32(int value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
int slot = 0xbaadbeef;
|
|
|
|
root->appendNew<MemoryValue>(
|
|
|
|
proc, Store, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), &slot), 0);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
|
|
|
|
|
|
|
|
CHECK(!compileAndRun<int>(proc, value));
|
|
|
|
CHECK(slot == value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStoreConstant(int value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
int slot = 0xbaadbeef;
|
|
|
|
root->appendNew<MemoryValue>(
|
|
|
|
proc, Store, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), value),
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), &slot), 0);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
|
|
|
|
|
|
|
|
CHECK(!compileAndRun<int>(proc));
|
|
|
|
CHECK(slot == value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStoreConstantPtr(intptr_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
intptr_t slot;
|
|
|
|
#if CPU(ADDRESS64)
|
|
|
|
slot = (static_cast<intptr_t>(0xbaadbeef) << 32) + static_cast<intptr_t>(0xbaadbeef);
|
|
|
|
#else
|
|
|
|
slot = 0xbaadbeef;
|
|
|
|
#endif
|
|
|
|
root->appendNew<MemoryValue>(
|
|
|
|
proc, Store, Origin(),
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), value),
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), &slot), 0);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
|
|
|
|
|
|
|
|
CHECK(!compileAndRun<int>(proc));
|
|
|
|
CHECK(slot == value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStore8Arg()
|
|
|
|
{
|
|
|
|
{ // Direct addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store8, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int8_t storage = 0;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, 42, &storage) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // Indexed addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
|
|
|
|
Value* displacement = root->appendNew<Const64Value>(proc, Origin(), -1);
|
|
|
|
|
|
|
|
Value* baseDisplacement = root->appendNew<Value>(proc, Add, Origin(), displacement, base);
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), baseDisplacement, offset);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store8, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int8_t storage = 0;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, 42, &storage, 1) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStore8Imm()
|
|
|
|
{
|
|
|
|
{ // Direct addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const32Value>(proc, Origin(), 42);
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store8, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int8_t storage = 0;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, &storage) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // Indexed addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const32Value>(proc, Origin(), 42);
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* displacement = root->appendNew<Const64Value>(proc, Origin(), -1);
|
|
|
|
|
|
|
|
Value* baseDisplacement = root->appendNew<Value>(proc, Add, Origin(), displacement, base);
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), baseDisplacement, offset);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store8, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int8_t storage = 0;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, &storage, 1) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStorePartial8BitRegisterOnX86()
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
// We want to have this in ECX.
|
|
|
|
Value* returnValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
|
|
|
|
// We want this suck in EDX.
|
|
|
|
Value* whereToStore = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
|
|
|
|
// The patch point is there to help us force the hand of the compiler.
|
|
|
|
PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
|
|
|
|
|
|
|
|
// For the value above to be materialized and give the allocator
|
|
|
|
// a stronger insentive to name those register the way we need.
|
|
|
|
patchpoint->append(ConstrainedValue(returnValue, ValueRep(GPRInfo::regT3)));
|
|
|
|
patchpoint->append(ConstrainedValue(whereToStore, ValueRep(GPRInfo::regT2)));
|
|
|
|
|
|
|
|
// We'll produce EDI.
|
B3 should support tuple types
https://bugs.webkit.org/show_bug.cgi?id=200327
Reviewed by Filip Pizlo.
As part of the Wasm multi-value proposal, we need to teach B3 that
patchpoints can return more than one value. This is done by
adding a new B3::Type called Tuple. Unlike, other B3 types Tuple
is actually an encoded index into a numeric B3::Type vector on the
procedure. This lets us distinguish any two tuples from each
other, moreover, it's possible to get the vector of types with
just the B3::Tuple type and the procedure.
Since most B3 operations only expect to see a single numeric child
there is a new Opcode, Extract, that takes yields the some, fixed,
entry from a tuple value. Extract would be the only other change
needed to make tuples work in B3 except that some optimizations
expect to be able to take any non-Void value and stick it into a
Variable of the same type. This means both Get/Set from a variable
have to support Tuples as well. For simplicity and consistency,
the ability to accept tuples is also applied to Phi and Upsilon.
In order to lower a Tuple, B3Lowering needs to have a Tmp for each
nested type in a Tuple. While we could reuse the existing
IndexedTables to hold the extra information we need to lower
Tuples, we instead use a two new HashTables for Value->Tmp(s) and
Phi->Tmp(s). It's expected that Tuples will be sufficiently
uncommon the overhead of tracking everything together would be
prohibitive. On the other hand, we don't worry about this for
Variables because we don't expect those to make it to lowering.
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* b3/B3Bank.h:
(JSC::B3::bankForType):
* b3/B3CheckValue.cpp:
(JSC::B3::CheckValue::CheckValue):
* b3/B3ExtractValue.cpp: Copied from Source/JavaScriptCore/b3/B3ProcedureInlines.h.
(JSC::B3::ExtractValue::~ExtractValue):
(JSC::B3::ExtractValue::dumpMeta const):
* b3/B3ExtractValue.h: Copied from Source/JavaScriptCore/b3/B3FixSSA.h.
* b3/B3FixSSA.h:
* b3/B3LowerMacros.cpp:
* b3/B3LowerMacrosAfterOptimizations.cpp:
* b3/B3LowerToAir.cpp:
* b3/B3NativeTraits.h:
* b3/B3Opcode.cpp:
(JSC::B3::invertedCompare):
(WTF::printInternal):
* b3/B3Opcode.h:
(JSC::B3::opcodeForConstant):
* b3/B3PatchpointSpecial.cpp:
(JSC::B3::PatchpointSpecial::forEachArg):
(JSC::B3::PatchpointSpecial::isValid):
(JSC::B3::PatchpointSpecial::admitsStack):
(JSC::B3::PatchpointSpecial::generate):
* b3/B3PatchpointValue.cpp:
(JSC::B3::PatchpointValue::dumpMeta const):
(JSC::B3::PatchpointValue::PatchpointValue):
* b3/B3PatchpointValue.h:
* b3/B3Procedure.cpp:
(JSC::B3::Procedure::addTuple):
(JSC::B3::Procedure::isValidTuple const):
(JSC::B3::Procedure::tupleForType const):
(JSC::B3::Procedure::addIntConstant):
(JSC::B3::Procedure::addConstant):
* b3/B3Procedure.h:
(JSC::B3::Procedure::returnCount const):
* b3/B3ProcedureInlines.h:
(JSC::B3::Procedure::extractFromTuple const):
* b3/B3ReduceStrength.cpp:
* b3/B3StackmapSpecial.cpp:
(JSC::B3::StackmapSpecial::isValidImpl):
(JSC::B3::StackmapSpecial::isArgValidForType):
(JSC::B3::StackmapSpecial::isArgValidForRep):
(JSC::B3::StackmapSpecial::isArgValidForValue): Deleted.
* b3/B3StackmapSpecial.h:
* b3/B3StackmapValue.h:
* b3/B3Type.cpp:
(WTF::printInternal):
* b3/B3Type.h:
(JSC::B3::Type::Type):
(JSC::B3::Type::tupleFromIndex):
(JSC::B3::Type::kind const):
(JSC::B3::Type::tupleIndex const):
(JSC::B3::Type::hash const):
(JSC::B3::Type::operator== const):
(JSC::B3::Type::operator!= const):
(JSC::B3::Type::isInt const):
(JSC::B3::Type::isFloat const):
(JSC::B3::Type::isNumeric const):
(JSC::B3::Type::isTuple const):
(JSC::B3::sizeofType):
(JSC::B3::isInt): Deleted.
(JSC::B3::isFloat): Deleted.
* b3/B3TypeMap.h:
(JSC::B3::TypeMap::at):
* b3/B3Validate.cpp:
* b3/B3Value.cpp:
(JSC::B3::Value::isRounded const):
(JSC::B3::Value::effects const):
(JSC::B3::Value::typeFor):
* b3/B3Value.h:
* b3/B3ValueInlines.h:
* b3/B3ValueKey.cpp:
(JSC::B3::ValueKey::intConstant):
* b3/B3ValueKey.h:
(JSC::B3::ValueKey::hash const):
* b3/B3ValueRep.h:
* b3/B3Width.h:
(JSC::B3::widthForType):
* b3/air/AirArg.cpp:
(JSC::B3::Air::Arg::canRepresent const):
* b3/air/AirArg.h:
* b3/air/AirCCallingConvention.cpp:
(JSC::B3::Air::cCallResult):
* b3/air/AirLowerMacros.cpp:
(JSC::B3::Air::lowerMacros):
* b3/testb3.h:
(populateWithInterestingValues):
* b3/testb3_1.cpp:
(run):
* b3/testb3_3.cpp:
(testStorePartial8BitRegisterOnX86):
* b3/testb3_5.cpp:
(testPatchpointWithRegisterResult):
(testPatchpointWithStackArgumentResult):
(testPatchpointWithAnyResult):
* b3/testb3_6.cpp:
(testPatchpointDoubleRegs):
(testSomeEarlyRegister):
* b3/testb3_7.cpp:
(testShuffleDoesntTrashCalleeSaves):
(testReportUsedRegistersLateUseFollowedByEarlyDefDoesNotMarkUseAsDead):
(testSimpleTuplePair):
(testSimpleTuplePairUnused):
(testSimpleTuplePairStack):
(tailDupedTuplePair):
(tuplePairVariableLoop):
(tupleNestedLoop):
(addTupleTests):
* b3/testb3_8.cpp:
(testLoad):
(addLoadTests):
* ftl/FTLAbbreviatedTypes.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstruct):
(JSC::FTL::DFG::LowerDFGToB3::compileDirectCallOrConstruct):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargsSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToB3::compileCallEval):
(JSC::FTL::DFG::LowerDFGToB3::compileCPUIntrinsic):
(JSC::FTL::DFG::LowerDFGToB3::compileInstanceOf):
(JSC::FTL::DFG::LowerDFGToB3::compileCallDOMGetter):
(JSC::FTL::DFG::LowerDFGToB3::emitBinarySnippet):
(JSC::FTL::DFG::LowerDFGToB3::emitBinaryBitOpSnippet):
(JSC::FTL::DFG::LowerDFGToB3::emitRightShiftSnippet):
(JSC::FTL::DFG::LowerDFGToB3::allocateHeapCell):
* wasm/WasmAirIRGenerator.cpp:
(JSC::Wasm::AirIRGenerator::emitPatchpoint):
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
* wasm/WasmCallingConvention.h:
(JSC::Wasm::CallingConvention::marshallArgument const):
(JSC::Wasm::CallingConvention::setupFrameInPrologue const):
(JSC::Wasm::CallingConvention::setupCall const):
(JSC::Wasm::CallingConventionAir::setupCall const):
Canonical link: https://commits.webkit.org/214174@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@248178 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2019-08-02 21:02:05 +00:00
|
|
|
patchpoint->resultConstraints = { ValueRep::reg(GPRInfo::regT6) };
|
2019-08-01 01:01:53 +00:00
|
|
|
|
|
|
|
// Give the allocator a good reason not to use any other register.
|
|
|
|
RegisterSet clobberSet = RegisterSet::allGPRs();
|
|
|
|
clobberSet.exclude(RegisterSet::stackRegisters());
|
|
|
|
clobberSet.exclude(RegisterSet::reservedHardwareRegisters());
|
|
|
|
clobberSet.clear(GPRInfo::regT3);
|
|
|
|
clobberSet.clear(GPRInfo::regT2);
|
|
|
|
clobberSet.clear(GPRInfo::regT6);
|
|
|
|
patchpoint->clobberLate(clobberSet);
|
|
|
|
|
|
|
|
// Set EDI.
|
|
|
|
patchpoint->setGenerator(
|
|
|
|
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
|
|
|
|
AllowMacroScratchRegisterUsage allowScratch(jit);
|
|
|
|
jit.xor64(params[0].gpr(), params[0].gpr());
|
|
|
|
});
|
|
|
|
|
|
|
|
// If everything went well, we should have the big number in eax,
|
|
|
|
// patchpoint == EDI and whereToStore = EDX.
|
|
|
|
// Since EDI == 5, and AH = 5 on 8 bit store, this would go wrong
|
|
|
|
// if we use X86 partial registers.
|
|
|
|
root->appendNew<MemoryValue>(proc, Store8, Origin(), patchpoint, whereToStore);
|
|
|
|
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), returnValue);
|
|
|
|
|
|
|
|
int8_t storage = 0xff;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, 0x12345678abcdef12, &storage) == 0x12345678abcdef12);
|
|
|
|
CHECK(!storage);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStore16Arg()
|
|
|
|
{
|
|
|
|
{ // Direct addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store16, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int16_t storage = -1;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, 42, &storage) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // Indexed addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Value>(proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
|
|
|
|
Value* displacement = root->appendNew<Const64Value>(proc, Origin(), -1);
|
|
|
|
|
|
|
|
Value* baseDisplacement = root->appendNew<Value>(proc, Add, Origin(), displacement, base);
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), baseDisplacement, offset);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store16, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int16_t storage = -1;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, 42, &storage, 1) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStore16Imm()
|
|
|
|
{
|
|
|
|
{ // Direct addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const32Value>(proc, Origin(), 42);
|
|
|
|
Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store16, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int16_t storage = -1;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, &storage) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // Indexed addressing.
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
|
|
|
|
Value* value = root->appendNew<Const32Value>(proc, Origin(), 42);
|
|
|
|
Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
|
|
|
|
Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
|
|
|
|
Value* displacement = root->appendNew<Const64Value>(proc, Origin(), -1);
|
|
|
|
|
|
|
|
Value* baseDisplacement = root->appendNew<Value>(proc, Add, Origin(), displacement, base);
|
|
|
|
Value* address = root->appendNew<Value>(proc, Add, Origin(), baseDisplacement, offset);
|
|
|
|
|
|
|
|
root->appendNew<MemoryValue>(proc, Store16, Origin(), value, address);
|
|
|
|
root->appendNewControlValue(proc, Return, Origin(), value);
|
|
|
|
|
|
|
|
int16_t storage = -1;
|
|
|
|
CHECK(compileAndRun<int64_t>(proc, &storage, 1) == 42);
|
|
|
|
CHECK(storage == 42);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testTrunc(int64_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, value) == static_cast<int>(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAdd1(int value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Add, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), 1)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int>(proc, value) == value + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testAdd1Ptr(intptr_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Add, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), 1)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<intptr_t>(proc, value) == value + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testNeg32(int32_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Sub, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), 0),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<int32_t>(proc, value) == -value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testNegPtr(intptr_t value)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Sub, Origin(),
|
|
|
|
root->appendNew<ConstPtrValue>(proc, Origin(), 0),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
|
|
|
|
|
|
|
|
CHECK(compileAndRun<intptr_t>(proc, value) == -value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testStoreAddLoad32(int amount)
|
|
|
|
{
|
|
|
|
Procedure proc;
|
|
|
|
BasicBlock* root = proc.addBlock();
|
|
|
|
int slot = 37;
|
|
|
|
ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
|
|
|
|
root->appendNew<MemoryValue>(
|
|
|
|
proc, Store, Origin(),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Add, Origin(),
|
|
|
|
root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr),
|
|
|
|
root->appendNew<Value>(
|
|
|
|
proc, Trunc, Origin(),
|
|
|
|
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
|
|
|
|
slotPtr, 0);
|
|
|
|
root->appendNewControlValue(
|
|
|
|
proc, Return, Origin(),
|
|
|
|
root->appendNew<Const32Value>(proc, Origin(), 0));
|
|
|
|
|
|
|
|
CHECK(!compileAndRun<int>(proc, amount));
|
|
|
|
CHECK(slot == 37 + amount);
|
|
|
|
}
|
|
|
|
|
2019-08-01 05:58:09 +00:00
|
|
|
// Make sure the compiler does not try to optimize anything out.
|
|
|
|
static NEVER_INLINE double zero()
|
|
|
|
{
|
|
|
|
return 0.;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double negativeZero()
|
|
|
|
{
|
|
|
|
return -zero();
|
|
|
|
}
|
|
|
|
|
|
|
|
void addArgTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
|
|
|
|
{
|
|
|
|
RUN(testAddArg(111));
|
|
|
|
RUN(testAddArgs(1, 1));
|
|
|
|
RUN(testAddArgs(1, 2));
|
|
|
|
RUN(testAddArgImm(1, 2));
|
|
|
|
RUN(testAddArgImm(0, 2));
|
|
|
|
RUN(testAddArgImm(1, 0));
|
|
|
|
RUN(testAddImmArg(1, 2));
|
|
|
|
RUN(testAddImmArg(0, 2));
|
|
|
|
RUN(testAddImmArg(1, 0));
|
|
|
|
RUN_BINARY(testAddArgMem, int64Operands(), int64Operands());
|
|
|
|
RUN_BINARY(testAddMemArg, int64Operands(), int64Operands());
|
|
|
|
RUN_BINARY(testAddImmMem, int64Operands(), int64Operands());
|
|
|
|
RUN_UNARY(testAddArg32, int32Operands());
|
|
|
|
RUN(testAddArgs32(1, 1));
|
|
|
|
RUN(testAddArgs32(1, 2));
|
|
|
|
RUN_BINARY(testAddArgMem32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testAddMemArg32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testAddImmMem32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testAddNeg1, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testAddNeg2, int32Operands(), int32Operands());
|
|
|
|
RUN(testAddArgZeroImmZDef());
|
|
|
|
RUN(testAddLoadTwice());
|
|
|
|
RUN_TERNARY(testAddMulMulArgs, int64Operands(), int64Operands(), int64Operands());
|
|
|
|
|
|
|
|
RUN(testAddArgDouble(M_PI));
|
|
|
|
RUN(testAddArgsDouble(M_PI, 1));
|
|
|
|
RUN(testAddArgsDouble(M_PI, -M_PI));
|
|
|
|
RUN(testAddArgImmDouble(M_PI, 1));
|
|
|
|
RUN(testAddArgImmDouble(M_PI, 0));
|
|
|
|
RUN(testAddArgImmDouble(M_PI, negativeZero()));
|
|
|
|
RUN(testAddArgImmDouble(0, 0));
|
|
|
|
RUN(testAddArgImmDouble(0, negativeZero()));
|
|
|
|
RUN(testAddArgImmDouble(negativeZero(), 0));
|
|
|
|
RUN(testAddArgImmDouble(negativeZero(), negativeZero()));
|
|
|
|
RUN(testAddImmArgDouble(M_PI, 1));
|
|
|
|
RUN(testAddImmArgDouble(M_PI, 0));
|
|
|
|
RUN(testAddImmArgDouble(M_PI, negativeZero()));
|
|
|
|
RUN(testAddImmArgDouble(0, 0));
|
|
|
|
RUN(testAddImmArgDouble(0, negativeZero()));
|
|
|
|
RUN(testAddImmArgDouble(negativeZero(), 0));
|
|
|
|
RUN(testAddImmArgDouble(negativeZero(), negativeZero()));
|
|
|
|
RUN(testAddImmsDouble(M_PI, 1));
|
|
|
|
RUN(testAddImmsDouble(M_PI, 0));
|
|
|
|
RUN(testAddImmsDouble(M_PI, negativeZero()));
|
|
|
|
RUN(testAddImmsDouble(0, 0));
|
|
|
|
RUN(testAddImmsDouble(0, negativeZero()));
|
|
|
|
RUN(testAddImmsDouble(negativeZero(), negativeZero()));
|
|
|
|
RUN_UNARY(testAddArgFloat, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testAddArgsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testAddFPRArgsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testAddArgImmFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testAddImmArgFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testAddImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_UNARY(testAddArgFloatWithUselessDoubleConversion, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testAddArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testAddArgsFloatWithEffectfulDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
|
|
|
|
RUN(testMulArg(5));
|
|
|
|
RUN(testMulAddArg(5));
|
|
|
|
RUN(testMulAddArg(85));
|
|
|
|
RUN(testMulArgStore(5));
|
|
|
|
RUN(testMulArgStore(85));
|
|
|
|
RUN(testMulArgs(1, 1));
|
|
|
|
RUN(testMulArgs(1, 2));
|
|
|
|
RUN(testMulArgs(3, 3));
|
|
|
|
RUN(testMulArgImm(1, 2));
|
|
|
|
RUN(testMulArgImm(1, 4));
|
|
|
|
RUN(testMulArgImm(1, 8));
|
|
|
|
RUN(testMulArgImm(1, 16));
|
|
|
|
RUN(testMulArgImm(1, 0x80000000llu));
|
|
|
|
RUN(testMulArgImm(1, 0x800000000000llu));
|
|
|
|
RUN(testMulArgImm(7, 2));
|
|
|
|
RUN(testMulArgImm(7, 4));
|
|
|
|
RUN(testMulArgImm(7, 8));
|
|
|
|
RUN(testMulArgImm(7, 16));
|
|
|
|
RUN(testMulArgImm(7, 0x80000000llu));
|
|
|
|
RUN(testMulArgImm(7, 0x800000000000llu));
|
|
|
|
RUN(testMulArgImm(-42, 2));
|
|
|
|
RUN(testMulArgImm(-42, 4));
|
|
|
|
RUN(testMulArgImm(-42, 8));
|
|
|
|
RUN(testMulArgImm(-42, 16));
|
|
|
|
RUN(testMulArgImm(-42, 0x80000000llu));
|
|
|
|
RUN(testMulArgImm(-42, 0x800000000000llu));
|
|
|
|
RUN(testMulArgImm(0, 2));
|
|
|
|
RUN(testMulArgImm(1, 0));
|
|
|
|
RUN(testMulArgImm(3, 3));
|
|
|
|
RUN(testMulArgImm(3, -1));
|
|
|
|
RUN(testMulArgImm(-3, -1));
|
|
|
|
RUN(testMulArgImm(0, -1));
|
|
|
|
RUN(testMulImmArg(1, 2));
|
|
|
|
RUN(testMulImmArg(0, 2));
|
|
|
|
RUN(testMulImmArg(1, 0));
|
|
|
|
RUN(testMulImmArg(3, 3));
|
2019-08-01 21:30:39 +00:00
|
|
|
RUN_BINARY(testMulImm32SignExtend, int32Operands(), int32Operands());
|
2019-08-01 05:58:09 +00:00
|
|
|
RUN(testMulImm32SignExtend(0xFFFFFFFE, 0xFFFFFFFF));
|
|
|
|
RUN(testMulImm32SignExtend(0xFFFFFFFF, 0xFFFFFFFE));
|
|
|
|
RUN(testMulArgs32(1, 1));
|
|
|
|
RUN(testMulArgs32(1, 2));
|
|
|
|
RUN(testMulArgs32(0xFFFFFFFF, 0xFFFFFFFF));
|
|
|
|
RUN(testMulArgs32(0xFFFFFFFE, 0xFFFFFFFF));
|
Add a new pattern to instruction selector to utilize UMULL supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=228721
Reviewed by Saam Barati.
Unsigned Multiply Long (UMULL) multiplies two 32-bit register values, and writes the
result to the destination register. This instruction is an alias of the UMADDL instruction.
umull xd wn wm
The equivalent pattern is: d = ZExt32(n) * ZExt32(m)
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = Trunc(@0)
Int @2 = ArgumentReg(%x1)
Int @3 = Trunc(@2)
Int @4 = ZExt32(@1)
Int @5 = ZExt32(@3)
Int @6 = Mul(@4, @5)
Void@7 = Return(@6, Terminal)
// Old optimized AIR
Move %x0, %x0, @4
Move %x1, %x1, @5
Mul %x0, %x1, %x0, @6
Ret %x0, @7
// New optimized AIR
MultiplyZeroExtend %x0, %x1, %x0, @6
Ret %x0, @7
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::multiplyZeroExtend32):
* assembler/testmasm.cpp:
(JSC::testMultiplyZeroExtend32):
* b3/B3LowerToAir.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testMulArgs32SignExtend):
(testMulArgs32ZeroExtend):
* b3/testb3_3.cpp:
(addArgTests):
Canonical link: https://commits.webkit.org/240205@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280583 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-08-03 04:18:22 +00:00
|
|
|
RUN(testMulArgs32SignExtend());
|
|
|
|
RUN(testMulArgs32ZeroExtend());
|
2019-08-01 05:58:09 +00:00
|
|
|
RUN(testMulLoadTwice());
|
|
|
|
RUN(testMulAddArgsLeft());
|
|
|
|
RUN(testMulAddArgsRight());
|
|
|
|
RUN(testMulAddArgsLeft32());
|
|
|
|
RUN(testMulAddArgsRight32());
|
Add a new pattern to instruction selector to utilize SMADDL supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227188
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-22
Reviewed by Saam Barati.
Signed Multiply-Add Long(SMADDL), supported by ARM64, multiplies two 32-bit
register values, adds a 64-bit register value, and writes the result to the
64-bit destination register. The instruction selector can utilize this to
lowering certain patterns in B3 IR before further Air optimization.
Given the operation:
smaddl d, n, m, a
The equivalent patterns would be:
d = a + SExt32(n) * SExt32(m)
d = SExt32(n) * SExt32(m) + a
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = SExt32(Trunc(ArgumentReg(%x1)))
Int @2 = SExt32(Trunc(ArgumentReg(%x2)))
Int @3 = Mul(@1, @2)
Int @4 = Add(@0, @3)
Void@5 = Return(@4, Terminal)
Before Adding SMADDL:
// Old optimized AIR
SignExtend32ToPtr %x1, %x1, @1
SignExtend32ToPtr %x2, %x2, @2
MultiplyAdd64 %x1, %x2, %x0, %x0, @4
Ret64 %x0, @5
After Adding SMADDL:
// New optimized AIR
MultiplyAddSignExtend32 %x1, %x2, %x0, %x0, @8
Ret64 %x0, @9
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::multiplyAddSignExtend32):
* assembler/testmasm.cpp:
(JSC::testMultiplyAddSignExtend32Left):
(JSC::testMultiplyAddSignExtend32Right):
* b3/B3LowerToAir.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testMulAddArg):
(testMulAddArgsLeft):
(testMulAddArgsRight):
(testMulAddSignExtend32ArgsLeft):
(testMulAddSignExtend32ArgsRight):
(testMulAddArgsLeft32):
(testMulAddArgsRight32):
* b3/testb3_3.cpp:
(addArgTests):
Canonical link: https://commits.webkit.org/239048@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279134 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-22 18:53:40 +00:00
|
|
|
RUN(testMulAddSignExtend32ArgsLeft());
|
|
|
|
RUN(testMulAddSignExtend32ArgsRight());
|
Add SMNEGL, UMNEGL, UMADDL, and UMSUBL for ARM64 and select this instruction in Air
https://bugs.webkit.org/show_bug.cgi?id=227857
Reviewed by Robin Morisset.
The previous patches have already added MNEG, MADD, MSUB, SMADDL, and SMSUBL.
This patch completes the corresponding signed or unsigned variants (SMNEGL,
UMNEGL, UMADDL, and UMSUBL) of them. In addition, this patch refactors
the implementation and the associative test cases of MADD, MSUB, and MNEG
to be more readable and maintainable w.r.t their variants.
------------------------------
### SMNEGL/UMNEGL Xd Wn Wm ###
------------------------------
Signed/Unsigned Multiply-Negate Long multiplies two 32-bit register values,
negates the product, and writes the result to the 64-bit destination register.
The equivalent patterns are
d = -(SExt32(n) * SExt32(m)) and d = -(ZExt32(n) * ZExt32(m)) respectively.
Given B3 IR:
Int @0 = S/ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = S/ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = Mul(@0, @1)
Int @3 = Neg(@2)
Void@4 = Return(@3, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @0
Move32 %x1, %x1, @1
MultiplyNeg %x0, %x1, %x0, @3
Ret %x0, @4
// New optimized AIR
MultiplyNegSign/ZeroExtend %x0, %x1, %x0, @3
Ret %x0, @4
--------------------------
### UMADDL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Add Long multiplies two 32-bit register values, adds a 64-bit
register value, and writes the result to the 64-bit destination register. The
equivalent patterns are
d = ZExt32(n) * ZExt32(m) + a or d = a + ZExt32(n) * ZExt32(m)
Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Add(@3, @2)
Void@5 = Return(@4, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @1
Move32 %x1, %x1, @2
MultiplyAdd %x0, %x1, %x2, %x0, @4
Ret64 %x0, @5
// New optimized AIR
MultiplyAddZeroExtend %x0, %x1, %x2, %x0, @8
Ret %x0, @9
--------------------------
### UMSUBL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Subtract Long multiplies two 32-bit register values, subtracts
the product from a 64-bit register value, and writes the result to the 64-bit
destination register. The equivalent patterns are
d = a - ZExt32(n) * ZExt32(m)
Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Sub(@2, @3)
Void@5 = Return(@4, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @1
Move32 %x1, %x1, @2
MultiplySub %x0, %x1, %x2, %x0, @4
Ret64 %x0, @5
// New optimized AIR
MultiplySubZeroExtend %x0, %x1, %x2, %x0, @8
Ret %x0, @9
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::multiplyNeg32):
(JSC::MacroAssemblerARM64::multiplyAddZeroExtend32):
(JSC::MacroAssemblerARM64::multiplySubZeroExtend32):
(JSC::MacroAssemblerARM64::multiplyNeg64):
(JSC::MacroAssemblerARM64::multiplyNegSignExtend32):
(JSC::MacroAssemblerARM64::multiplyNegZeroExtend32):
* assembler/testmasm.cpp:
(JSC::testMultiplyAddSignExtend32):
(JSC::testMultiplyAddZeroExtend32):
(JSC::testMultiplySubSignExtend32):
(JSC::testMultiplySubZeroExtend32):
(JSC::testMultiplyNegSignExtend32):
(JSC::testMultiplyNegZeroExtend32):
(JSC::testMultiplyAddSignExtend32Left): Deleted.
(JSC::testMultiplyAddSignExtend32Right): Deleted.
* b3/B3LowerToAir.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testMulAddArgsLeft):
(testMulAddArgsRight):
(testMulAddSignExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsRight):
(testMulSubArgsLeft):
(testMulSubArgsRight):
(testMulSubArgsRight32):
(testMulSubSignExtend32):
(testMulSubZeroExtend32):
(testMulNegArgArg):
(testMulNegArgs):
(testMulNegArgs32):
(testMulNegSignExtend32):
(testMulNegZeroExtend32):
(testMulSubSignExtend32Args): Deleted.
* b3/testb3_3.cpp:
(addArgTests):
Canonical link: https://commits.webkit.org/239605@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279850 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-07-12 20:51:11 +00:00
|
|
|
RUN(testMulAddZeroExtend32ArgsLeft());
|
|
|
|
RUN(testMulAddZeroExtend32ArgsRight());
|
2019-08-01 05:58:09 +00:00
|
|
|
RUN(testMulSubArgsLeft());
|
|
|
|
RUN(testMulSubArgsRight());
|
|
|
|
RUN(testMulSubArgsLeft32());
|
|
|
|
RUN(testMulSubArgsRight32());
|
Add SMNEGL, UMNEGL, UMADDL, and UMSUBL for ARM64 and select this instruction in Air
https://bugs.webkit.org/show_bug.cgi?id=227857
Reviewed by Robin Morisset.
The previous patches have already added MNEG, MADD, MSUB, SMADDL, and SMSUBL.
This patch completes the corresponding signed or unsigned variants (SMNEGL,
UMNEGL, UMADDL, and UMSUBL) of them. In addition, this patch refactors
the implementation and the associative test cases of MADD, MSUB, and MNEG
to be more readable and maintainable w.r.t their variants.
------------------------------
### SMNEGL/UMNEGL Xd Wn Wm ###
------------------------------
Signed/Unsigned Multiply-Negate Long multiplies two 32-bit register values,
negates the product, and writes the result to the 64-bit destination register.
The equivalent patterns are
d = -(SExt32(n) * SExt32(m)) and d = -(ZExt32(n) * ZExt32(m)) respectively.
Given B3 IR:
Int @0 = S/ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = S/ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = Mul(@0, @1)
Int @3 = Neg(@2)
Void@4 = Return(@3, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @0
Move32 %x1, %x1, @1
MultiplyNeg %x0, %x1, %x0, @3
Ret %x0, @4
// New optimized AIR
MultiplyNegSign/ZeroExtend %x0, %x1, %x0, @3
Ret %x0, @4
--------------------------
### UMADDL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Add Long multiplies two 32-bit register values, adds a 64-bit
register value, and writes the result to the 64-bit destination register. The
equivalent patterns are
d = ZExt32(n) * ZExt32(m) + a or d = a + ZExt32(n) * ZExt32(m)
Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Add(@3, @2)
Void@5 = Return(@4, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @1
Move32 %x1, %x1, @2
MultiplyAdd %x0, %x1, %x2, %x0, @4
Ret64 %x0, @5
// New optimized AIR
MultiplyAddZeroExtend %x0, %x1, %x2, %x0, @8
Ret %x0, @9
--------------------------
### UMSUBL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Subtract Long multiplies two 32-bit register values, subtracts
the product from a 64-bit register value, and writes the result to the 64-bit
destination register. The equivalent patterns are
d = a - ZExt32(n) * ZExt32(m)
Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Sub(@2, @3)
Void@5 = Return(@4, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @1
Move32 %x1, %x1, @2
MultiplySub %x0, %x1, %x2, %x0, @4
Ret64 %x0, @5
// New optimized AIR
MultiplySubZeroExtend %x0, %x1, %x2, %x0, @8
Ret %x0, @9
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::multiplyNeg32):
(JSC::MacroAssemblerARM64::multiplyAddZeroExtend32):
(JSC::MacroAssemblerARM64::multiplySubZeroExtend32):
(JSC::MacroAssemblerARM64::multiplyNeg64):
(JSC::MacroAssemblerARM64::multiplyNegSignExtend32):
(JSC::MacroAssemblerARM64::multiplyNegZeroExtend32):
* assembler/testmasm.cpp:
(JSC::testMultiplyAddSignExtend32):
(JSC::testMultiplyAddZeroExtend32):
(JSC::testMultiplySubSignExtend32):
(JSC::testMultiplySubZeroExtend32):
(JSC::testMultiplyNegSignExtend32):
(JSC::testMultiplyNegZeroExtend32):
(JSC::testMultiplyAddSignExtend32Left): Deleted.
(JSC::testMultiplyAddSignExtend32Right): Deleted.
* b3/B3LowerToAir.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testMulAddArgsLeft):
(testMulAddArgsRight):
(testMulAddSignExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsRight):
(testMulSubArgsLeft):
(testMulSubArgsRight):
(testMulSubArgsRight32):
(testMulSubSignExtend32):
(testMulSubZeroExtend32):
(testMulNegArgArg):
(testMulNegArgs):
(testMulNegArgs32):
(testMulNegSignExtend32):
(testMulNegZeroExtend32):
(testMulSubSignExtend32Args): Deleted.
* b3/testb3_3.cpp:
(addArgTests):
Canonical link: https://commits.webkit.org/239605@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279850 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-07-12 20:51:11 +00:00
|
|
|
RUN(testMulSubSignExtend32());
|
|
|
|
RUN(testMulSubZeroExtend32());
|
2019-08-01 05:58:09 +00:00
|
|
|
RUN(testMulNegArgs());
|
|
|
|
RUN(testMulNegArgs32());
|
Add SMNEGL, UMNEGL, UMADDL, and UMSUBL for ARM64 and select this instruction in Air
https://bugs.webkit.org/show_bug.cgi?id=227857
Reviewed by Robin Morisset.
The previous patches have already added MNEG, MADD, MSUB, SMADDL, and SMSUBL.
This patch completes the corresponding signed or unsigned variants (SMNEGL,
UMNEGL, UMADDL, and UMSUBL) of them. In addition, this patch refactors
the implementation and the associative test cases of MADD, MSUB, and MNEG
to be more readable and maintainable w.r.t their variants.
------------------------------
### SMNEGL/UMNEGL Xd Wn Wm ###
------------------------------
Signed/Unsigned Multiply-Negate Long multiplies two 32-bit register values,
negates the product, and writes the result to the 64-bit destination register.
The equivalent patterns are
d = -(SExt32(n) * SExt32(m)) and d = -(ZExt32(n) * ZExt32(m)) respectively.
Given B3 IR:
Int @0 = S/ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = S/ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = Mul(@0, @1)
Int @3 = Neg(@2)
Void@4 = Return(@3, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @0
Move32 %x1, %x1, @1
MultiplyNeg %x0, %x1, %x0, @3
Ret %x0, @4
// New optimized AIR
MultiplyNegSign/ZeroExtend %x0, %x1, %x0, @3
Ret %x0, @4
--------------------------
### UMADDL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Add Long multiplies two 32-bit register values, adds a 64-bit
register value, and writes the result to the 64-bit destination register. The
equivalent patterns are
d = ZExt32(n) * ZExt32(m) + a or d = a + ZExt32(n) * ZExt32(m)
Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Add(@3, @2)
Void@5 = Return(@4, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @1
Move32 %x1, %x1, @2
MultiplyAdd %x0, %x1, %x2, %x0, @4
Ret64 %x0, @5
// New optimized AIR
MultiplyAddZeroExtend %x0, %x1, %x2, %x0, @8
Ret %x0, @9
--------------------------
### UMSUBL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Subtract Long multiplies two 32-bit register values, subtracts
the product from a 64-bit register value, and writes the result to the 64-bit
destination register. The equivalent patterns are
d = a - ZExt32(n) * ZExt32(m)
Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Sub(@2, @3)
Void@5 = Return(@4, Terminal)
// Old optimized AIR
Move32 %x0, %x0, @1
Move32 %x1, %x1, @2
MultiplySub %x0, %x1, %x2, %x0, @4
Ret64 %x0, @5
// New optimized AIR
MultiplySubZeroExtend %x0, %x1, %x2, %x0, @8
Ret %x0, @9
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::multiplyNeg32):
(JSC::MacroAssemblerARM64::multiplyAddZeroExtend32):
(JSC::MacroAssemblerARM64::multiplySubZeroExtend32):
(JSC::MacroAssemblerARM64::multiplyNeg64):
(JSC::MacroAssemblerARM64::multiplyNegSignExtend32):
(JSC::MacroAssemblerARM64::multiplyNegZeroExtend32):
* assembler/testmasm.cpp:
(JSC::testMultiplyAddSignExtend32):
(JSC::testMultiplyAddZeroExtend32):
(JSC::testMultiplySubSignExtend32):
(JSC::testMultiplySubZeroExtend32):
(JSC::testMultiplyNegSignExtend32):
(JSC::testMultiplyNegZeroExtend32):
(JSC::testMultiplyAddSignExtend32Left): Deleted.
(JSC::testMultiplyAddSignExtend32Right): Deleted.
* b3/B3LowerToAir.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testMulAddArgsLeft):
(testMulAddArgsRight):
(testMulAddSignExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsRight):
(testMulSubArgsLeft):
(testMulSubArgsRight):
(testMulSubArgsRight32):
(testMulSubSignExtend32):
(testMulSubZeroExtend32):
(testMulNegArgArg):
(testMulNegArgs):
(testMulNegArgs32):
(testMulNegSignExtend32):
(testMulNegZeroExtend32):
(testMulSubSignExtend32Args): Deleted.
* b3/testb3_3.cpp:
(addArgTests):
Canonical link: https://commits.webkit.org/239605@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279850 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-07-12 20:51:11 +00:00
|
|
|
RUN(testMulNegSignExtend32());
|
|
|
|
RUN(testMulNegZeroExtend32());
|
2019-08-01 05:58:09 +00:00
|
|
|
|
|
|
|
RUN_BINARY(testMulArgNegArg, int64Operands(), int64Operands())
|
|
|
|
RUN_BINARY(testMulNegArgArg, int64Operands(), int64Operands())
|
|
|
|
RUN_UNARY(testMulArgDouble, floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testMulArgsDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testMulArgImmDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testMulImmArgDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testMulImmsDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_UNARY(testMulArgFloat, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testMulArgsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testMulArgImmFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testMulImmArgFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testMulImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_UNARY(testMulArgFloatWithUselessDoubleConversion, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testMulArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testMulArgsFloatWithEffectfulDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
|
|
|
|
RUN(testDivArgDouble(M_PI));
|
|
|
|
RUN(testDivArgsDouble(M_PI, 1));
|
|
|
|
RUN(testDivArgsDouble(M_PI, -M_PI));
|
|
|
|
RUN(testDivArgImmDouble(M_PI, 1));
|
|
|
|
RUN(testDivArgImmDouble(M_PI, 0));
|
|
|
|
RUN(testDivArgImmDouble(M_PI, negativeZero()));
|
|
|
|
RUN(testDivArgImmDouble(0, 0));
|
|
|
|
RUN(testDivArgImmDouble(0, negativeZero()));
|
|
|
|
RUN(testDivArgImmDouble(negativeZero(), 0));
|
|
|
|
RUN(testDivArgImmDouble(negativeZero(), negativeZero()));
|
|
|
|
RUN(testDivImmArgDouble(M_PI, 1));
|
|
|
|
RUN(testDivImmArgDouble(M_PI, 0));
|
|
|
|
RUN(testDivImmArgDouble(M_PI, negativeZero()));
|
|
|
|
RUN(testDivImmArgDouble(0, 0));
|
|
|
|
RUN(testDivImmArgDouble(0, negativeZero()));
|
|
|
|
RUN(testDivImmArgDouble(negativeZero(), 0));
|
|
|
|
RUN(testDivImmArgDouble(negativeZero(), negativeZero()));
|
|
|
|
RUN(testDivImmsDouble(M_PI, 1));
|
|
|
|
RUN(testDivImmsDouble(M_PI, 0));
|
|
|
|
RUN(testDivImmsDouble(M_PI, negativeZero()));
|
|
|
|
RUN(testDivImmsDouble(0, 0));
|
|
|
|
RUN(testDivImmsDouble(0, negativeZero()));
|
|
|
|
RUN(testDivImmsDouble(negativeZero(), negativeZero()));
|
|
|
|
RUN_UNARY(testDivArgFloat, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testDivArgsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testDivArgImmFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testDivImmArgFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testDivImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_UNARY(testDivArgFloatWithUselessDoubleConversion, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testDivArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testDivArgsFloatWithEffectfulDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
|
|
|
|
RUN_BINARY(testUDivArgsInt32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testUDivArgsInt64, int64Operands(), int64Operands());
|
|
|
|
|
|
|
|
RUN_UNARY(testModArgDouble, floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testModArgsDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testModArgImmDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testModImmArgDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testModImmsDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_UNARY(testModArgFloat, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testModArgsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testModArgImmFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testModImmArgFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testModImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
|
|
|
|
RUN_BINARY(testUModArgsInt32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testUModArgsInt64, int64Operands(), int64Operands());
|
|
|
|
|
|
|
|
RUN(testSubArg(24));
|
|
|
|
RUN(testSubArgs(1, 1));
|
|
|
|
RUN(testSubArgs(1, 2));
|
|
|
|
RUN(testSubArgs(13, -42));
|
|
|
|
RUN(testSubArgs(-13, 42));
|
|
|
|
RUN(testSubArgImm(1, 1));
|
|
|
|
RUN(testSubArgImm(1, 2));
|
|
|
|
RUN(testSubArgImm(13, -42));
|
|
|
|
RUN(testSubArgImm(-13, 42));
|
|
|
|
RUN(testSubArgImm(42, 0));
|
|
|
|
RUN(testSubImmArg(1, 1));
|
|
|
|
RUN(testSubImmArg(1, 2));
|
|
|
|
RUN(testSubImmArg(13, -42));
|
|
|
|
RUN(testSubImmArg(-13, 42));
|
|
|
|
RUN_BINARY(testSubArgMem, int64Operands(), int64Operands());
|
|
|
|
RUN_BINARY(testSubMemArg, int64Operands(), int64Operands());
|
|
|
|
RUN_BINARY(testSubImmMem, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testSubMemImm, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testSubNeg, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testNegSub, int32Operands(), int32Operands());
|
|
|
|
RUN_UNARY(testNegValueSubOne, int32Operands());
|
|
|
|
RUN_BINARY(testNegMulArgImm, int64Operands(), int64Operands());
|
|
|
|
RUN_TERNARY(testSubMulMulArgs, int64Operands(), int64Operands(), int64Operands());
|
|
|
|
|
|
|
|
RUN_TERNARY(testSubSub, int32Operands(), int32Operands(), int32Operands());
|
|
|
|
RUN_TERNARY(testSubSub2, int32Operands(), int32Operands(), int32Operands());
|
|
|
|
RUN_TERNARY(testSubAdd, int32Operands(), int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testSubFirstNeg, int32Operands(), int32Operands());
|
|
|
|
|
|
|
|
RUN(testSubArgs32(1, 1));
|
|
|
|
RUN(testSubArgs32(1, 2));
|
|
|
|
RUN(testSubArgs32(13, -42));
|
|
|
|
RUN(testSubArgs32(-13, 42));
|
Add Air opcode sub32/64(Reg, Imm, Reg) form for ARM64 and select this instruction in Air
https://bugs.webkit.org/show_bug.cgi?id=226937
Patch by Yijia Huang <yijia_huang@apple.com> on 2021-06-16
Reviewed by Yusuke Suzuki.
Previously, Air arm64 sub32/64 utilize sub(Imm, Tmp) at optlevel = 0 and
add(Tmp, -Imm) at optlevel > 0 to perform and optimize sub(Tmp, Imm, Tmp).
The issue with this is that we were not eliding redundant operations.
For example:
// B3 IR
@0 = Trunc(ArgumentReg(0))
@1 = Const
@2 = Sub(@0, @1)
@3 = Return(@2)
// Old optimized Air IR
// OptLevel = 0
Move %x0, %tmp1, @0
Move $Const, %tmp2, @1
Move %tmp1, %tmp0, @2 // Redundant
Sub $Const, %tmp0, @2
Move %tmp0, %x0, @3
Ret32 %x0, @3
To remove those redundant instructions, Air arm64 sub32/64 opcode should
indicate a new form sub(Tmp, Imm, Tmp).
// New optimized Air IR
// OptLevel = 0
Move %x0, %tmp1, @0
Move $Const, %tmp2, @1
Sub %tmp1, $Const, %tmp0, @2
Move %tmp0, %x0, @3
Ret32 %x0, @3
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::sub32):
(JSC::MacroAssemblerARM64::sub64):
* assembler/testmasm.cpp:
(JSC::testSub32Args):
(JSC::testSub32Imm):
(JSC::testSub32ArgImm):
(JSC::testSub64Imm32):
(JSC::testSub64ArgImm32):
(JSC::testSub64Imm64):
(JSC::testSub64ArgImm64):
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testSubArgs32ZeroExtend):
* b3/testb3_3.cpp:
(addArgTests):
Canonical link: https://commits.webkit.org/238879@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@278950 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-06-16 19:14:26 +00:00
|
|
|
RUN(testSubArgs32ZeroExtend(1, 1));
|
|
|
|
RUN(testSubArgs32ZeroExtend(1, 2));
|
|
|
|
RUN(testSubArgs32ZeroExtend(13, -42));
|
|
|
|
RUN(testSubArgs32ZeroExtend(-13, 42));
|
2019-08-01 05:58:09 +00:00
|
|
|
RUN(testSubArgImm32(1, 1));
|
|
|
|
RUN(testSubArgImm32(1, 2));
|
|
|
|
RUN(testSubArgImm32(13, -42));
|
|
|
|
RUN(testSubArgImm32(-13, 42));
|
|
|
|
RUN(testSubImmArg32(1, 1));
|
|
|
|
RUN(testSubImmArg32(1, 2));
|
|
|
|
RUN(testSubImmArg32(13, -42));
|
|
|
|
RUN(testSubImmArg32(-13, 42));
|
|
|
|
RUN_BINARY(testSubArgMem32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testSubMemArg32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testSubImmMem32, int32Operands(), int32Operands());
|
|
|
|
RUN_BINARY(testSubMemImm32, int32Operands(), int32Operands());
|
|
|
|
RUN_UNARY(testNegValueSubOne32, int64Operands());
|
|
|
|
|
|
|
|
RUN_UNARY(testSubArgDouble, floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testSubArgsDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testSubArgImmDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testSubImmArgDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_BINARY(testSubImmsDouble, floatingPointOperands<double>(), floatingPointOperands<double>());
|
|
|
|
RUN_UNARY(testSubArgFloat, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testSubArgsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testSubArgImmFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testSubImmArgFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testSubImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_UNARY(testSubArgFloatWithUselessDoubleConversion, floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testSubArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN_BINARY(testSubArgsFloatWithEffectfulDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addCallTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
|
|
|
|
{
|
|
|
|
RUN(testCallSimple(1, 2));
|
|
|
|
RUN(testCallRare(1, 2));
|
|
|
|
RUN(testCallRareLive(1, 2, 3));
|
|
|
|
RUN(testCallSimplePure(1, 2));
|
|
|
|
RUN(testCallFunctionWithHellaArguments());
|
|
|
|
RUN(testCallFunctionWithHellaArguments2());
|
|
|
|
RUN(testCallFunctionWithHellaArguments3());
|
|
|
|
|
|
|
|
RUN(testReturnDouble(0.0));
|
|
|
|
RUN(testReturnDouble(negativeZero()));
|
|
|
|
RUN(testReturnDouble(42.5));
|
|
|
|
RUN_UNARY(testReturnFloat, floatingPointOperands<float>());
|
|
|
|
|
|
|
|
RUN(testCallSimpleDouble(1, 2));
|
|
|
|
RUN(testCallFunctionWithHellaDoubleArguments());
|
|
|
|
RUN_BINARY(testCallSimpleFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
|
|
|
|
RUN(testCallFunctionWithHellaFloatArguments());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addShrTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
|
|
|
|
{
|
|
|
|
RUN(testSShrArgs(1, 0));
|
|
|
|
RUN(testSShrArgs(1, 1));
|
|
|
|
RUN(testSShrArgs(1, 62));
|
|
|
|
RUN(testSShrArgs(0xffffffffffffffff, 0));
|
|
|
|
RUN(testSShrArgs(0xffffffffffffffff, 1));
|
|
|
|
RUN(testSShrArgs(0xffffffffffffffff, 63));
|
|
|
|
RUN(testSShrImms(1, 0));
|
|
|
|
RUN(testSShrImms(1, 1));
|
|
|
|
RUN(testSShrImms(1, 62));
|
|
|
|
RUN(testSShrImms(1, 65));
|
|
|
|
RUN(testSShrImms(0xffffffffffffffff, 0));
|
|
|
|
RUN(testSShrImms(0xffffffffffffffff, 1));
|
|
|
|
RUN(testSShrImms(0xffffffffffffffff, 63));
|
|
|
|
RUN(testSShrArgImm(1, 0));
|
|
|
|
RUN(testSShrArgImm(1, 1));
|
|
|
|
RUN(testSShrArgImm(1, 62));
|
|
|
|
RUN(testSShrArgImm(1, 65));
|
|
|
|
RUN(testSShrArgImm(0xffffffffffffffff, 0));
|
|
|
|
RUN(testSShrArgImm(0xffffffffffffffff, 1));
|
|
|
|
RUN(testSShrArgImm(0xffffffffffffffff, 63));
|
|
|
|
RUN(testSShrArg32(32));
|
|
|
|
RUN(testSShrArgs32(1, 0));
|
|
|
|
RUN(testSShrArgs32(1, 1));
|
|
|
|
RUN(testSShrArgs32(1, 62));
|
|
|
|
RUN(testSShrArgs32(1, 33));
|
|
|
|
RUN(testSShrArgs32(0xffffffff, 0));
|
|
|
|
RUN(testSShrArgs32(0xffffffff, 1));
|
|
|
|
RUN(testSShrArgs32(0xffffffff, 63));
|
|
|
|
RUN(testSShrImms32(1, 0));
|
|
|
|
RUN(testSShrImms32(1, 1));
|
|
|
|
RUN(testSShrImms32(1, 62));
|
|
|
|
RUN(testSShrImms32(1, 33));
|
|
|
|
RUN(testSShrImms32(0xffffffff, 0));
|
|
|
|
RUN(testSShrImms32(0xffffffff, 1));
|
|
|
|
RUN(testSShrImms32(0xffffffff, 63));
|
|
|
|
RUN(testSShrArgImm32(1, 0));
|
|
|
|
RUN(testSShrArgImm32(1, 1));
|
|
|
|
RUN(testSShrArgImm32(1, 62));
|
|
|
|
RUN(testSShrArgImm32(0xffffffff, 0));
|
|
|
|
RUN(testSShrArgImm32(0xffffffff, 1));
|
|
|
|
RUN(testSShrArgImm32(0xffffffff, 63));
|
|
|
|
|
|
|
|
RUN(testZShrArgs(1, 0));
|
|
|
|
RUN(testZShrArgs(1, 1));
|
|
|
|
RUN(testZShrArgs(1, 62));
|
|
|
|
RUN(testZShrArgs(0xffffffffffffffff, 0));
|
|
|
|
RUN(testZShrArgs(0xffffffffffffffff, 1));
|
|
|
|
RUN(testZShrArgs(0xffffffffffffffff, 63));
|
|
|
|
RUN(testZShrImms(1, 0));
|
|
|
|
RUN(testZShrImms(1, 1));
|
|
|
|
RUN(testZShrImms(1, 62));
|
|
|
|
RUN(testZShrImms(1, 65));
|
|
|
|
RUN(testZShrImms(0xffffffffffffffff, 0));
|
|
|
|
RUN(testZShrImms(0xffffffffffffffff, 1));
|
|
|
|
RUN(testZShrImms(0xffffffffffffffff, 63));
|
|
|
|
RUN(testZShrArgImm(1, 0));
|
|
|
|
RUN(testZShrArgImm(1, 1));
|
|
|
|
RUN(testZShrArgImm(1, 62));
|
|
|
|
RUN(testZShrArgImm(1, 65));
|
|
|
|
RUN(testZShrArgImm(0xffffffffffffffff, 0));
|
|
|
|
RUN(testZShrArgImm(0xffffffffffffffff, 1));
|
|
|
|
RUN(testZShrArgImm(0xffffffffffffffff, 63));
|
|
|
|
RUN(testZShrArg32(32));
|
|
|
|
RUN(testZShrArgs32(1, 0));
|
|
|
|
RUN(testZShrArgs32(1, 1));
|
|
|
|
RUN(testZShrArgs32(1, 62));
|
|
|
|
RUN(testZShrArgs32(1, 33));
|
|
|
|
RUN(testZShrArgs32(0xffffffff, 0));
|
|
|
|
RUN(testZShrArgs32(0xffffffff, 1));
|
|
|
|
RUN(testZShrArgs32(0xffffffff, 63));
|
|
|
|
RUN(testZShrImms32(1, 0));
|
|
|
|
RUN(testZShrImms32(1, 1));
|
|
|
|
RUN(testZShrImms32(1, 62));
|
|
|
|
RUN(testZShrImms32(1, 33));
|
|
|
|
RUN(testZShrImms32(0xffffffff, 0));
|
|
|
|
RUN(testZShrImms32(0xffffffff, 1));
|
|
|
|
RUN(testZShrImms32(0xffffffff, 63));
|
|
|
|
RUN(testZShrArgImm32(1, 0));
|
|
|
|
RUN(testZShrArgImm32(1, 1));
|
|
|
|
RUN(testZShrArgImm32(1, 62));
|
|
|
|
RUN(testZShrArgImm32(0xffffffff, 0));
|
|
|
|
RUN(testZShrArgImm32(0xffffffff, 1));
|
|
|
|
RUN(testZShrArgImm32(0xffffffff, 63));
|
Add Pre/Post-Indexed Address Mode to Air for ARM64
https://bugs.webkit.org/show_bug.cgi?id=228047
Reviewed by Phil Pizlo.
Pre-indexed addressing means that the address is the sum of the value in the 64-bit base register
and an offset, and the address is then written back to the base register. And post-indexed
addressing means that the address is the value in the 64-bit base register, and the sum of the
address and the offset is then written back to the base register. They are relatively common for
loops to iterate over an array by increasing/decreasing a pointer into the array at each iteration.
With such an addressing mode, the instruction selector can merge the increment and access the array.
#####################################
## Pre-Index Address Mode For Load ##
#####################################
LDR Wt, [Xn, #imm]!
In B3 Reduction Strength, since we have this reduction rule:
Turn this: Load(Add(address, offset1), offset = offset2)
Into this: Load(address, offset = offset1 + offset2)
Then, the equivalent pattern is:
address = Add(base, offset)
...
memory = Load(base, offset)
First, we convert it to the canonical form:
address = Add(base, offset)
newMemory = Load(base, offset) // move the memory to just after the address
...
memory = Identity(newMemory)
Next, lower to Air:
Move %base, %address
Move (%address, prefix(offset)), %newMemory
######################################
## Post-Index Address Mode For Load ##
######################################
LDR Wt, [Xn], #imm
Then, the equivalent pattern is:
memory = Load(base, 0)
...
address = Add(base, offset)
First, we convert it to the canonical form:
newOffset = Constant
newAddress = Add(base, offset)
memory = Load(base, 0) // move the offset and address to just before the memory
...
offset = Identity(newOffset)
address = Identity(newAddress)
Next, lower to Air:
Move %base, %newAddress
Move (%newAddress, postfix(offset)), %memory
#############################
## Pattern Match Algorithm ##
#############################
To detect the pattern for prefix/postfix increment address is tricky due to the structure in B3 IR. The
algorithm used in this patch is to collect the first valid values (add/load), then search for any
paired value (load/add) to match all of them. In worst case, the runtime complexity is O(n^2)
when n is the number of all values.
After collecting two sets of candidates, we match the prefix incremental address first since it seems
more beneficial to the compiler (shown in the next section). And then, go for the postfix one.
##############################################
## Test for Pre/Post-Increment Address Mode ##
##############################################
Given Loop with Pre-Increment:
int64_t ldr_pre(int64_t *p) {
int64_t res = 0;
while (res < 10)
res += *++p;
return res;
}
B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
Int64 b@0 = Const64(0)
Int64 b@2 = ArgumentReg(%x0)
Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
Int64 b@18 = Phi(ReadsLocalState)
Int64 b@19 = Phi(ReadsLocalState)
Int64 b@7 = Const64(10)
Int32 b@8 = AboveEqual(b@18, $10(b@7))
Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
Int64 b@10 = Const64(8)
Int64 b@11 = Add(b@19, $8(b@10))
Int64 b@13 = Load(b@11, ControlDependent|Reads:Top)
Int64 b@14 = Add(b@18, b@13)
Void b@22 = Upsilon(b@14, ^18, WritesLocalState)
Void b@23 = Upsilon(b@11, ^19, WritesLocalState)
Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
Void b@17 = Return(b@18, Terminal)
Variables:
Int64 var0
Int64 var1
------------------------------------------------------
W/O Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
Move $8, %x3, $8(b@12)
Add64 $8, %x0, %x1, b@11
Move (%x0,%x3), %x0, b@13
Add64 %x0, %x2, %x2, b@14
Move %x1, %x0, b@23
Jump b@16
Successors: #1
...
------------------------------------------------------
W/ Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
MoveWithIncrement64 (%x0,Pre($8)), %x2, b@13
Add64 %x2, %x1, %x1, b@14
Jump b@16
Successors: #1
...
------------------------------------------------------
Given Loop with Post-Increment:
int64_t ldr_pre(int64_t *p) {
int64_t res = 0;
while (res < 10)
res += *p++;
return res;
}
B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
Int64 b@0 = Const64(0)
Int64 b@2 = ArgumentReg(%x0)
Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
Int64 b@18 = Phi(ReadsLocalState)
Int64 b@19 = Phi(ReadsLocalState)
Int64 b@7 = Const64(10)
Int32 b@8 = AboveEqual(b@18, $10(b@7))
Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
Int64 b@10 = Load(b@19, ControlDependent|Reads:Top)
Int64 b@11 = Add(b@18, b@10)
Int64 b@12 = Const64(8)
Int64 b@13 = Add(b@19, $8(b@12))
Void b@22 = Upsilon(b@11, ^18, WritesLocalState)
Void b@23 = Upsilon(b@13, ^19, WritesLocalState)
Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
Void b@17 = Return(b@18, Terminal)
Variables:
Int64 var0
Int64 var1
------------------------------------------------------
W/O Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
Move (%x0), %x2, b@10
Add64 %x2, %x1, %x1, b@11
Add64 $8, %x0, %x0, b@13
Jump b@16
Successors: #1
...
------------------------------------------------------
W/ Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
MoveWithIncrement64 (%x0,Post($8)), %x2, b@10
Add64 %x2, %x1, %x1, b@11
Jump b@16
Successors: #1
...
------------------------------------------------------
* Sources.txt:
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::PreIndexAddress::PreIndexAddress):
(JSC::AbstractMacroAssembler::PostIndexAddress::PostIndexAddress):
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::load64):
(JSC::MacroAssemblerARM64::load32):
(JSC::MacroAssemblerARM64::store64):
(JSC::MacroAssemblerARM64::store32):
* assembler/testmasm.cpp:
(JSC::testStorePrePostIndex32):
(JSC::testStorePrePostIndex64):
(JSC::testLoadPrePostIndex32):
(JSC::testLoadPrePostIndex64):
* b3/B3CanonicalizePrePostIncrements.cpp: Added.
(JSC::B3::canonicalizePrePostIncrements):
* b3/B3CanonicalizePrePostIncrements.h: Copied from Source/JavaScriptCore/b3/B3ValueKeyInlines.h.
* b3/B3Generate.cpp:
(JSC::B3::generateToAir):
* b3/B3LowerToAir.cpp:
* b3/B3ValueKey.h:
* b3/B3ValueKeyInlines.h:
(JSC::B3::ValueKey::ValueKey):
* b3/air/AirArg.cpp:
(JSC::B3::Air::Arg::jsHash const):
(JSC::B3::Air::Arg::dump const):
(WTF::printInternal):
* b3/air/AirArg.h:
(JSC::B3::Air::Arg::preIndex):
(JSC::B3::Air::Arg::postIndex):
(JSC::B3::Air::Arg::isPreIndex const):
(JSC::B3::Air::Arg::isPostIndex const):
(JSC::B3::Air::Arg::isMemory const):
(JSC::B3::Air::Arg::base const):
(JSC::B3::Air::Arg::offset const):
(JSC::B3::Air::Arg::isGP const):
(JSC::B3::Air::Arg::isFP const):
(JSC::B3::Air::Arg::isValidPreIndexForm):
(JSC::B3::Air::Arg::isValidPostIndexForm):
(JSC::B3::Air::Arg::isValidForm const):
(JSC::B3::Air::Arg::forEachTmpFast):
(JSC::B3::Air::Arg::forEachTmp):
(JSC::B3::Air::Arg::asPreIndexAddress const):
(JSC::B3::Air::Arg::asPostIndexAddress const):
* b3/air/AirOpcode.opcodes:
* b3/air/opcode_generator.rb:
* b3/testb3.h:
* b3/testb3_3.cpp:
(testLoadPreIndex32):
(testLoadPreIndex64):
(testLoadPostIndex32):
(testLoadPostIndex64):
(addShrTests):
* jit/ExecutableAllocator.cpp:
(JSC::jitWriteThunkGenerator):
Canonical link: https://commits.webkit.org/240125@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280493 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-07-30 20:44:47 +00:00
|
|
|
|
2021-08-18 02:27:03 +00:00
|
|
|
if (Options::useB3CanonicalizePrePostIncrements()) {
|
|
|
|
RUN(testLoadPreIndex32());
|
|
|
|
RUN(testLoadPreIndex64());
|
|
|
|
RUN(testLoadPostIndex32());
|
|
|
|
RUN(testLoadPostIndex64());
|
|
|
|
|
|
|
|
RUN(testStorePreIndex32());
|
|
|
|
RUN(testStorePreIndex64());
|
|
|
|
RUN(testStorePostIndex32());
|
|
|
|
RUN(testStorePostIndex64());
|
|
|
|
}
|
2019-08-01 05:58:09 +00:00
|
|
|
}
|
|
|
|
|
2019-08-01 01:01:53 +00:00
|
|
|
#endif // ENABLE(B3_JIT)
|