| /* |
| * 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) |
| |
| void testStoreRelAddLoadAcq32(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, 0, HeapRange(42), HeapRange(42)), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| slotPtr, 0, HeapRange(42), HeapRange(42)); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| if (isARM64()) { |
| checkUsesInstruction(*code, "lda"); |
| checkUsesInstruction(*code, "stl"); |
| } |
| if (isX86()) |
| checkUsesInstruction(*code, "xchg"); |
| CHECK(!invoke<int>(*code, amount)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoadImm32(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<Const32Value>(proc, Origin(), amount)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoad8(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int8_t slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| root->appendNew<MemoryValue>( |
| proc, Store8, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, 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); |
| } |
| |
| void testStoreRelAddLoadAcq8(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int8_t slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| root->appendNew<MemoryValue>( |
| proc, Store8, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, loadOpcode, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| slotPtr, 0, HeapRange(42), HeapRange(42)); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| if (isARM64()) { |
| checkUsesInstruction(*code, "lda"); |
| checkUsesInstruction(*code, "stl"); |
| } |
| if (isX86()) |
| checkUsesInstruction(*code, "xchg"); |
| CHECK(!invoke<int>(*code, amount)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreRelAddFenceLoadAcq8(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int8_t slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| Value* loadedValue = root->appendNew<MemoryValue>( |
| proc, loadOpcode, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Void, Origin()); |
| patchpoint->clobber(RegisterSet::macroScratchRegisters()); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams&) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| jit.store8(CCallHelpers::TrustedImm32(0xbeef), &slot); |
| }); |
| patchpoint->effects = Effects::none(); |
| patchpoint->effects.fence = true; |
| root->appendNew<MemoryValue>( |
| proc, Store8, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| loadedValue, |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| slotPtr, 0, HeapRange(42), HeapRange(42)); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| if (isARM64()) { |
| checkUsesInstruction(*code, "lda"); |
| checkUsesInstruction(*code, "stl"); |
| } |
| if (isX86()) |
| checkUsesInstruction(*code, "xchg"); |
| CHECK(!invoke<int>(*code, amount)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoadImm8(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int8_t slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| root->appendNew<MemoryValue>( |
| proc, Store8, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr), |
| root->appendNew<Const32Value>(proc, Origin(), amount)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoad16(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int16_t slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| root->appendNew<MemoryValue>( |
| proc, Store16, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, 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); |
| } |
| |
| void testStoreRelAddLoadAcq16(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int16_t slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| root->appendNew<MemoryValue>( |
| proc, Store16, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, loadOpcode, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| slotPtr, 0, HeapRange(42), HeapRange(42)); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| if (isARM64()) { |
| checkUsesInstruction(*code, "lda"); |
| checkUsesInstruction(*code, "stl"); |
| } |
| if (isX86()) |
| checkUsesInstruction(*code, "xchg"); |
| CHECK(!invoke<int>(*code, amount)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoadImm16(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int16_t slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| root->appendNew<MemoryValue>( |
| proc, Store16, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr), |
| root->appendNew<Const32Value>(proc, Origin(), amount)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoad64(int amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int64_t slot = 37000000000ll; |
| 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, Int64, Origin(), slotPtr), |
| 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 == 37000000000ll + amount); |
| } |
| |
| void testStoreRelAddLoadAcq64(int amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int64_t slot = 37000000000ll; |
| 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, Int64, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| slotPtr, 0, HeapRange(42), HeapRange(42)); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| if (isARM64()) { |
| checkUsesInstruction(*code, "lda"); |
| checkUsesInstruction(*code, "stl"); |
| } |
| if (isX86()) |
| checkUsesInstruction(*code, "xchg"); |
| CHECK(!invoke<int>(*code, amount)); |
| CHECK(slot == 37000000000ll + amount); |
| } |
| |
| void testStoreAddLoadImm64(int64_t amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int64_t slot = 370000000000ll; |
| 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, Int64, Origin(), slotPtr), |
| root->appendNew<Const64Value>(proc, Origin(), amount)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 370000000000ll + amount); |
| } |
| |
| void testStoreAddLoad32Index(int amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int slot = 37; |
| int* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| 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); |
| } |
| |
| void testStoreAddLoadImm32Index(int amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int slot = 37; |
| int* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr), |
| root->appendNew<Const32Value>(proc, Origin(), amount)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoad8Index(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int8_t slot = 37; |
| int8_t* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| root->appendNew<MemoryValue>( |
| proc, Store8, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| slotPtr); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc, amount)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoadImm8Index(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int8_t slot = 37; |
| int8_t* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| root->appendNew<MemoryValue>( |
| proc, Store8, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr), |
| root->appendNew<Const32Value>(proc, Origin(), amount)), |
| slotPtr); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoad16Index(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int16_t slot = 37; |
| int16_t* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| root->appendNew<MemoryValue>( |
| proc, Store16, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| slotPtr); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc, amount)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoadImm16Index(int amount, B3::Opcode loadOpcode) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int16_t slot = 37; |
| int16_t* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| root->appendNew<MemoryValue>( |
| proc, Store16, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr), |
| root->appendNew<Const32Value>(proc, Origin(), amount)), |
| slotPtr); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddLoad64Index(int amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int64_t slot = 37000000000ll; |
| int64_t* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), slotPtr), |
| 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 == 37000000000ll + amount); |
| } |
| |
| void testStoreAddLoadImm64Index(int64_t amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int64_t slot = 370000000000ll; |
| int64_t* ptr = &slot; |
| intptr_t zero = 0; |
| Value* slotPtr = root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &zero))); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), slotPtr), |
| root->appendNew<Const64Value>(proc, Origin(), amount)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == 370000000000ll + amount); |
| } |
| |
| void testStoreSubLoad(int amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int32_t startValue = std::numeric_limits<int32_t>::min(); |
| int32_t slot = startValue; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Sub, 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 == startValue - amount); |
| } |
| |
| void testStoreAddLoadInterference(int amount) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int slot = 37; |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| ArgumentRegValue* otherSlotPtr = |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| MemoryValue* load = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 666), |
| otherSlotPtr, 0); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| load, root->appendNew<Const32Value>(proc, Origin(), amount)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc, &slot)); |
| CHECK(slot == 37 + amount); |
| } |
| |
| void testStoreAddAndLoad(int amount, int mask) |
| { |
| 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, BitAnd, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr), |
| root->appendNew<Const32Value>(proc, Origin(), amount)), |
| root->appendNew<Const32Value>(proc, Origin(), mask)), |
| slotPtr, 0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int>(proc)); |
| CHECK(slot == ((37 + amount) & mask)); |
| } |
| |
| void testStoreNegLoad32(int32_t value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| |
| int32_t slot = value; |
| |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Sub, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr)), |
| slotPtr, 0); |
| |
| root->appendNewControlValue( |
| proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int32_t>(proc)); |
| CHECK(slot == -value); |
| } |
| |
| void testStoreNegLoadPtr(intptr_t value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| |
| intptr_t slot = value; |
| |
| ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot); |
| |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Sub, Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), 0), |
| root->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), slotPtr)), |
| slotPtr, 0); |
| |
| root->appendNewControlValue( |
| proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(!compileAndRun<int32_t>(proc)); |
| CHECK(slot == -value); |
| } |
| |
| void testAdd1Uncommuted(int value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 1), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)))); |
| |
| CHECK(compileAndRun<int>(proc, value) == value + 1); |
| } |
| |
| void testLoadOffset() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int array[] = { 1, 2 }; |
| ConstPtrValue* arrayPtr = root->appendNew<ConstPtrValue>(proc, Origin(), array); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), arrayPtr, 0), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), arrayPtr, static_cast<int32_t>(sizeof(int))))); |
| |
| CHECK(compileAndRun<int>(proc) == array[0] + array[1]); |
| } |
| |
| void testLoadOffsetNotConstant() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int array[] = { 1, 2 }; |
| Value* arrayPtr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), arrayPtr, 0), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), arrayPtr, static_cast<int32_t>(sizeof(int))))); |
| |
| CHECK(compileAndRun<int>(proc, &array[0]) == array[0] + array[1]); |
| } |
| |
| void testLoadOffsetUsingAdd() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int array[] = { 1, 2 }; |
| ConstPtrValue* arrayPtr = root->appendNew<ConstPtrValue>(proc, Origin(), array); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), arrayPtr, |
| root->appendNew<ConstPtrValue>(proc, Origin(), 0))), |
| root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), arrayPtr, |
| root->appendNew<ConstPtrValue>(proc, Origin(), static_cast<int32_t>(sizeof(int))))))); |
| |
| CHECK(compileAndRun<int>(proc) == array[0] + array[1]); |
| } |
| |
| void testLoadOffsetUsingAddInterference() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int array[] = { 1, 2 }; |
| ConstPtrValue* arrayPtr = root->appendNew<ConstPtrValue>(proc, Origin(), array); |
| ArgumentRegValue* otherArrayPtr = |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Const32Value* theNumberOfTheBeast = root->appendNew<Const32Value>(proc, Origin(), 666); |
| MemoryValue* left = root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), arrayPtr, |
| root->appendNew<ConstPtrValue>(proc, Origin(), 0))); |
| MemoryValue* right = root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), arrayPtr, |
| root->appendNew<ConstPtrValue>(proc, Origin(), static_cast<int32_t>(sizeof(int))))); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), theNumberOfTheBeast, otherArrayPtr, 0); |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), theNumberOfTheBeast, otherArrayPtr, static_cast<int32_t>(sizeof(int))); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), left, right)); |
| |
| CHECK(compileAndRun<int>(proc, &array[0]) == 1 + 2); |
| CHECK(array[0] == 666); |
| CHECK(array[1] == 666); |
| } |
| |
| void testLoadOffsetUsingAddNotConstant() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int array[] = { 1, 2 }; |
| Value* arrayPtr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), arrayPtr, |
| root->appendNew<ConstPtrValue>(proc, Origin(), 0))), |
| root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), arrayPtr, |
| root->appendNew<ConstPtrValue>(proc, Origin(), static_cast<int32_t>(sizeof(int))))))); |
| |
| CHECK(compileAndRun<int>(proc, &array[0]) == array[0] + array[1]); |
| } |
| |
| void testLoadAddrShift(unsigned shift) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| int slots[2]; |
| |
| // Figure out which slot to use while having proper alignment for the shift. |
| int* slot = nullptr; |
| uintptr_t arg = 0; |
| for (unsigned i = sizeof(slots)/sizeof(slots[0]); i--;) { |
| slot = slots + i; |
| arg = bitwise_cast<uintptr_t>(slot) >> shift; |
| if (bitwise_cast<int*>(arg << shift) == slot) |
| break; |
| } |
| |
| *slot = 8675309; |
| |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<Value>( |
| proc, Shl, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0), |
| root->appendNew<Const32Value>(proc, Origin(), shift)))); |
| |
| CHECK(compileAndRun<int>(proc, arg) == 8675309); |
| } |
| |
| void testFramePointer() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<Value>(proc, FramePointer, Origin())); |
| |
| void* fp = compileAndRun<void*>(proc); |
| CHECK(fp < &proc); |
| CHECK(fp >= bitwise_cast<char*>(&proc) - 10000); |
| } |
| |
| void testOverrideFramePointer() |
| { |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| |
| // Add a stack slot to make the frame non trivial. |
| root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(8)); |
| |
| // Sub on x86 UseDef the source. If FP is not protected correctly, it will be overridden since it is the last visible use. |
| Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* fp = root->appendNew<Value>(proc, FramePointer, Origin()); |
| Value* result = root->appendNew<Value>(proc, Sub, Origin(), fp, offset); |
| |
| root->appendNewControlValue(proc, Return, Origin(), result); |
| CHECK(compileAndRun<int64_t>(proc, 1)); |
| } |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| |
| root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(8)); |
| |
| Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* fp = root->appendNew<Value>(proc, FramePointer, Origin()); |
| Value* offsetFP = root->appendNew<Value>(proc, BitAnd, Origin(), offset, fp); |
| Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| Value* offsetArg = root->appendNew<Value>(proc, Add, Origin(), offset, arg); |
| Value* result = root->appendNew<Value>(proc, Add, Origin(), offsetArg, offsetFP); |
| |
| root->appendNewControlValue(proc, Return, Origin(), result); |
| CHECK(compileAndRun<int64_t>(proc, 1, 2)); |
| } |
| } |
| |
| void testStackSlot() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(1))); |
| |
| void* stackSlot = compileAndRun<void*>(proc); |
| CHECK(stackSlot < &proc); |
| CHECK(stackSlot >= bitwise_cast<char*>(&proc) - 10000); |
| } |
| |
| void testLoadFromFramePointer() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<Value>(proc, FramePointer, Origin()))); |
| |
| void* fp = compileAndRun<void*>(proc); |
| void* myFP = __builtin_frame_address(0); |
| CHECK(fp <= myFP); |
| CHECK(fp >= bitwise_cast<char*>(myFP) - 10000); |
| } |
| |
| void testStoreLoadStackSlot(int value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| |
| SlotBaseValue* stack = |
| root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(sizeof(int))); |
| |
| root->appendNew<MemoryValue>( |
| proc, Store, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| stack, 0); |
| |
| root->appendNewControlValue( |
| proc, Return, Origin(), |
| root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), stack)); |
| |
| CHECK(compileAndRun<int>(proc, value) == value); |
| } |
| |
| void testStoreFloat(double input) |
| { |
| // Simple store from an address in a register. |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); |
| Value* argumentAsFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument); |
| |
| Value* destinationAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| root->appendNew<MemoryValue>(proc, Store, Origin(), argumentAsFloat, destinationAddress); |
| |
| root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| float output = 0.; |
| CHECK(!compileAndRun<int64_t>(proc, input, &output)); |
| CHECK(isIdentical(static_cast<float>(input), output)); |
| } |
| |
| // Simple indexed store. |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); |
| Value* argumentAsFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), argument); |
| |
| Value* destinationBaseAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* index = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| Value* scaledIndex = root->appendNew<Value>( |
| proc, Shl, Origin(), |
| index, |
| root->appendNew<Const32Value>(proc, Origin(), 2)); |
| Value* destinationAddress = root->appendNew<Value>(proc, Add, Origin(), scaledIndex, destinationBaseAddress); |
| |
| root->appendNew<MemoryValue>(proc, Store, Origin(), argumentAsFloat, destinationAddress); |
| |
| root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| float output = 0.; |
| CHECK(!compileAndRun<int64_t>(proc, input, &output - 1, 1)); |
| CHECK(isIdentical(static_cast<float>(input), output)); |
| } |
| } |
| |
| void testStoreDoubleConstantAsFloat(double input) |
| { |
| // Simple store from an address in a register. |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* value = root->appendNew<ConstDoubleValue>(proc, Origin(), input); |
| Value* valueAsFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), value); |
| |
| Value* destinationAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| |
| root->appendNew<MemoryValue>(proc, Store, Origin(), valueAsFloat, destinationAddress); |
| |
| root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| float output = 0.; |
| CHECK(!compileAndRun<int64_t>(proc, input, &output)); |
| CHECK(isIdentical(static_cast<float>(input), output)); |
| } |
| |
| void testSpillGP() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| |
| Vector<Value*> sources; |
| sources.append(root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| sources.append(root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| |
| for (unsigned i = 0; i < 30; ++i) { |
| sources.append( |
| root->appendNew<Value>(proc, Add, Origin(), sources[sources.size() - 1], sources[sources.size() - 2]) |
| ); |
| } |
| |
| Value* total = root->appendNew<Const64Value>(proc, Origin(), 0); |
| for (Value* value : sources) |
| total = root->appendNew<Value>(proc, Add, Origin(), total, value); |
| |
| root->appendNewControlValue(proc, Return, Origin(), total); |
| compileAndRun<int>(proc, 1, 2); |
| } |
| |
| void testSpillFP() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| |
| Vector<Value*> sources; |
| sources.append(root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0)); |
| sources.append(root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1)); |
| |
| for (unsigned i = 0; i < 30; ++i) { |
| sources.append( |
| root->appendNew<Value>(proc, Add, Origin(), sources[sources.size() - 1], sources[sources.size() - 2]) |
| ); |
| } |
| |
| Value* total = root->appendNew<ConstDoubleValue>(proc, Origin(), 0.); |
| for (Value* value : sources) |
| total = root->appendNew<Value>(proc, Add, Origin(), total, value); |
| |
| root->appendNewControlValue(proc, Return, Origin(), total); |
| compileAndRun<double>(proc, 1.1, 2.5); |
| } |
| |
| void testInt32ToDoublePartialRegisterStall() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* loop = proc.addBlock(); |
| BasicBlock* done = proc.addBlock(); |
| |
| // Head. |
| Value* total = root->appendNew<ConstDoubleValue>(proc, Origin(), 0.); |
| Value* counter = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| UpsilonValue* originalTotal = root->appendNew<UpsilonValue>(proc, Origin(), total); |
| UpsilonValue* originalCounter = root->appendNew<UpsilonValue>(proc, Origin(), counter); |
| root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loop)); |
| |
| // Loop. |
| Value* loopCounter = loop->appendNew<Value>(proc, Phi, Int64, Origin()); |
| Value* loopTotal = loop->appendNew<Value>(proc, Phi, Double, Origin()); |
| originalCounter->setPhi(loopCounter); |
| originalTotal->setPhi(loopTotal); |
| |
| Value* truncatedCounter = loop->appendNew<Value>(proc, Trunc, Origin(), loopCounter); |
| Value* doubleCounter = loop->appendNew<Value>(proc, IToD, Origin(), truncatedCounter); |
| Value* updatedTotal = loop->appendNew<Value>(proc, Add, Origin(), doubleCounter, loopTotal); |
| UpsilonValue* updatedTotalUpsilon = loop->appendNew<UpsilonValue>(proc, Origin(), updatedTotal); |
| updatedTotalUpsilon->setPhi(loopTotal); |
| |
| Value* decCounter = loop->appendNew<Value>(proc, Sub, Origin(), loopCounter, loop->appendNew<Const64Value>(proc, Origin(), 1)); |
| UpsilonValue* decCounterUpsilon = loop->appendNew<UpsilonValue>(proc, Origin(), decCounter); |
| decCounterUpsilon->setPhi(loopCounter); |
| loop->appendNewControlValue( |
| proc, Branch, Origin(), |
| decCounter, |
| FrequentedBlock(loop), FrequentedBlock(done)); |
| |
| // Tail. |
| done->appendNewControlValue(proc, Return, Origin(), updatedTotal); |
| CHECK(isIdentical(compileAndRun<double>(proc, 100000), 5000050000.)); |
| } |
| |
| void testInt32ToDoublePartialRegisterWithoutStall() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* loop = proc.addBlock(); |
| BasicBlock* done = proc.addBlock(); |
| |
| // Head. |
| Value* total = root->appendNew<ConstDoubleValue>(proc, Origin(), 0.); |
| Value* counter = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| UpsilonValue* originalTotal = root->appendNew<UpsilonValue>(proc, Origin(), total); |
| UpsilonValue* originalCounter = root->appendNew<UpsilonValue>(proc, Origin(), counter); |
| uint64_t forPaddingInput; |
| Value* forPaddingInputAddress = root->appendNew<ConstPtrValue>(proc, Origin(), &forPaddingInput); |
| uint64_t forPaddingOutput; |
| Value* forPaddingOutputAddress = root->appendNew<ConstPtrValue>(proc, Origin(), &forPaddingOutput); |
| root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loop)); |
| |
| // Loop. |
| Value* loopCounter = loop->appendNew<Value>(proc, Phi, Int64, Origin()); |
| Value* loopTotal = loop->appendNew<Value>(proc, Phi, Double, Origin()); |
| originalCounter->setPhi(loopCounter); |
| originalTotal->setPhi(loopTotal); |
| |
| Value* truncatedCounter = loop->appendNew<Value>(proc, Trunc, Origin(), loopCounter); |
| Value* doubleCounter = loop->appendNew<Value>(proc, IToD, Origin(), truncatedCounter); |
| Value* updatedTotal = loop->appendNew<Value>(proc, Add, Origin(), doubleCounter, loopTotal); |
| |
| // Add enough padding instructions to avoid a stall. |
| Value* loadPadding = loop->appendNew<MemoryValue>(proc, Load, Int64, Origin(), forPaddingInputAddress); |
| Value* padding = loop->appendNew<Value>(proc, BitXor, Origin(), loadPadding, loopCounter); |
| padding = loop->appendNew<Value>(proc, Add, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, BitOr, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, Sub, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, BitXor, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, Add, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, BitOr, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, Sub, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, BitXor, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, Add, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, BitOr, Origin(), padding, loopCounter); |
| padding = loop->appendNew<Value>(proc, Sub, Origin(), padding, loopCounter); |
| loop->appendNew<MemoryValue>(proc, Store, Origin(), padding, forPaddingOutputAddress); |
| |
| UpsilonValue* updatedTotalUpsilon = loop->appendNew<UpsilonValue>(proc, Origin(), updatedTotal); |
| updatedTotalUpsilon->setPhi(loopTotal); |
| |
| Value* decCounter = loop->appendNew<Value>(proc, Sub, Origin(), loopCounter, loop->appendNew<Const64Value>(proc, Origin(), 1)); |
| UpsilonValue* decCounterUpsilon = loop->appendNew<UpsilonValue>(proc, Origin(), decCounter); |
| decCounterUpsilon->setPhi(loopCounter); |
| loop->appendNewControlValue( |
| proc, Branch, Origin(), |
| decCounter, |
| FrequentedBlock(loop), FrequentedBlock(done)); |
| |
| // Tail. |
| done->appendNewControlValue(proc, Return, Origin(), updatedTotal); |
| CHECK(isIdentical(compileAndRun<double>(proc, 100000), 5000050000.)); |
| } |
| |
| void testBranch() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchPtr() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, static_cast<intptr_t>(42)) == 1); |
| CHECK(invoke<int>(*code, static_cast<intptr_t>(0)) == 0); |
| } |
| |
| void testDiamond() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| BasicBlock* done = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| UpsilonValue* thenResult = thenCase->appendNew<UpsilonValue>( |
| proc, Origin(), thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(done)); |
| |
| UpsilonValue* elseResult = elseCase->appendNew<UpsilonValue>( |
| proc, Origin(), elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(done)); |
| |
| Value* phi = done->appendNew<Value>(proc, Phi, Int32, Origin()); |
| thenResult->setPhi(phi); |
| elseResult->setPhi(phi); |
| done->appendNewControlValue(proc, Return, Origin(), phi); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchNotEqual() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, NotEqual, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| root->appendNew<Const32Value>(proc, Origin(), 0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchNotEqualCommute() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, NotEqual, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchNotEqualNotEqual() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, NotEqual, Origin(), |
| root->appendNew<Value>( |
| proc, NotEqual, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| root->appendNew<Const32Value>(proc, Origin(), 0)), |
| root->appendNew<Const32Value>(proc, Origin(), 0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchEqual() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| root->appendNew<Const32Value>(proc, Origin(), 0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchEqualEqual() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| root->appendNew<Const32Value>(proc, Origin(), 0)), |
| root->appendNew<Const32Value>(proc, Origin(), 0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchEqualCommute() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchEqualEqual1() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| root->appendNew<Const32Value>(proc, Origin(), 0)), |
| root->appendNew<Const32Value>(proc, Origin(), 1)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| auto code = compileProc(proc); |
| CHECK(invoke<int>(*code, 42) == 1); |
| CHECK(invoke<int>(*code, 0) == 0); |
| } |
| |
| void testBranchEqualOrUnorderedArgs(double a, double b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); |
| Value* argumentB = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1); |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB), |
| 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(), -13)); |
| |
| int64_t expected = (std::isunordered(a, b) || a == b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc, a, b) == expected); |
| } |
| |
| void testBranchEqualOrUnorderedArgs(float a, float b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* argumentB = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB), |
| 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(), -13)); |
| |
| int64_t expected = (std::isunordered(a, b) || a == b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc, &a, &b) == expected); |
| } |
| |
| void testBranchNotEqualAndOrderedArgs(double a, double b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); |
| Value* argumentB = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1); |
| Value* equalOrUnordered = root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB); |
| Value* notEqualAndOrdered = root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0), |
| equalOrUnordered); |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| notEqualAndOrdered, |
| 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(), -13)); |
| |
| int64_t expected = (!std::isunordered(a, b) && a != b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc, a, b) == expected); |
| } |
| |
| void testBranchNotEqualAndOrderedArgs(float a, float b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* argumentB = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| Value* equalOrUnordered = root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB); |
| Value* notEqualAndOrdered = root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), 0), |
| equalOrUnordered); |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| notEqualAndOrdered, |
| 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(), -13)); |
| |
| int64_t expected = (!std::isunordered(a, b) && a != b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc, &a, &b) == expected); |
| } |
| |
| void testBranchEqualOrUnorderedDoubleArgImm(double a, double b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); |
| Value* argumentB = root->appendNew<ConstDoubleValue>(proc, Origin(), b); |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB), |
| 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(), -13)); |
| |
| int64_t expected = (std::isunordered(a, b) || a == b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc, a) == expected); |
| } |
| |
| void testBranchEqualOrUnorderedFloatArgImm(float a, float b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* argumentB = root->appendNew<ConstFloatValue>(proc, Origin(), b); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB), |
| 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(), -13)); |
| |
| int64_t expected = (std::isunordered(a, b) || a == b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc, &a) == expected); |
| } |
| |
| void testBranchEqualOrUnorderedDoubleImms(double a, double b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<ConstDoubleValue>(proc, Origin(), a); |
| Value* argumentB = root->appendNew<ConstDoubleValue>(proc, Origin(), b); |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB), |
| 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(), -13)); |
| |
| int64_t expected = (std::isunordered(a, b) || a == b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc) == expected); |
| } |
| |
| void testBranchEqualOrUnorderedFloatImms(float a, float b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argumentA = root->appendNew<ConstFloatValue>(proc, Origin(), a); |
| Value* argumentB = root->appendNew<ConstFloatValue>(proc, Origin(), b); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argumentA, |
| argumentB), |
| 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(), -13)); |
| |
| int64_t expected = (std::isunordered(a, b) || a == b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc) == expected); |
| } |
| |
| void testBranchEqualOrUnorderedFloatWithUselessDoubleConversion(float a, float b) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* argument1 = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* argument2 = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| Value* argument1AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), argument1); |
| Value* argument2AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), argument2); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, EqualOrUnordered, Origin(), |
| argument1AsDouble, |
| argument2AsDouble), |
| 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(), -13)); |
| |
| int64_t expected = (std::isunordered(a, b) || a == b) ? 42 : -13; |
| CHECK(compileAndRun<int64_t>(proc, &a, &b) == expected); |
| } |
| |
| void testBranchFold(int value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), value), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(compileAndRun<int>(proc) == !!value); |
| } |
| |
| void testDiamondFold(int value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| BasicBlock* done = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Const32Value>(proc, Origin(), value), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| UpsilonValue* thenResult = thenCase->appendNew<UpsilonValue>( |
| proc, Origin(), thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(done)); |
| |
| UpsilonValue* elseResult = elseCase->appendNew<UpsilonValue>( |
| proc, Origin(), elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(done)); |
| |
| Value* phi = done->appendNew<Value>(proc, Phi, Int32, Origin()); |
| thenResult->setPhi(phi); |
| elseResult->setPhi(phi); |
| done->appendNewControlValue(proc, Return, Origin(), phi); |
| |
| CHECK(compileAndRun<int>(proc) == !!value); |
| } |
| |
| void testBranchNotEqualFoldPtr(intptr_t value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, NotEqual, Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), value), |
| root->appendNew<ConstPtrValue>(proc, Origin(), 0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(compileAndRun<int>(proc) == !!value); |
| } |
| |
| void testBranchEqualFoldPtr(intptr_t value) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Equal, Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), value), |
| root->appendNew<ConstPtrValue>(proc, Origin(), 0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| CHECK(compileAndRun<int>(proc) == !value); |
| } |
| |
| void testBranchLoadPtr() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, pointerType(), Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| intptr_t cond; |
| cond = 42; |
| CHECK(invoke<int>(*code, &cond) == 1); |
| cond = 0; |
| CHECK(invoke<int>(*code, &cond) == 0); |
| } |
| |
| void testBranchLoad32() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| int32_t cond; |
| cond = 42; |
| CHECK(invoke<int>(*code, &cond) == 1); |
| cond = 0; |
| CHECK(invoke<int>(*code, &cond) == 0); |
| } |
| |
| void testBranchLoad8S() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load8S, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| int8_t cond; |
| cond = -1; |
| CHECK(invoke<int>(*code, &cond) == 1); |
| cond = 0; |
| CHECK(invoke<int>(*code, &cond) == 0); |
| } |
| |
| void testBranchLoad8Z() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load8Z, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| uint8_t cond; |
| cond = 1; |
| CHECK(invoke<int>(*code, &cond) == 1); |
| cond = 0; |
| CHECK(invoke<int>(*code, &cond) == 0); |
| } |
| |
| void testBranchLoad16S() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load16S, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| int16_t cond; |
| cond = -1; |
| CHECK(invoke<int>(*code, &cond) == 1); |
| cond = 0; |
| CHECK(invoke<int>(*code, &cond) == 0); |
| } |
| |
| void testBranchLoad16Z() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load16Z, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| uint16_t cond; |
| cond = 1; |
| CHECK(invoke<int>(*code, &cond) == 1); |
| cond = 0; |
| CHECK(invoke<int>(*code, &cond) == 0); |
| } |
| |
| void testBranch8WithLoad8ZIndex() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| int logScale = 1; |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| root->appendNew<Value>( |
| proc, Above, Origin(), |
| root->appendNew<MemoryValue>( |
| proc, Load8Z, Origin(), |
| root->appendNew<Value>( |
| proc, Add, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0), |
| root->appendNew<Value>( |
| proc, Shl, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1), |
| root->appendNew<Const32Value>(proc, Origin(), logScale)))), |
| root->appendNew<Const32Value>(proc, Origin(), 250)), |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| uint32_t cond; |
| cond = 0xffffffffU; // All bytes are 0xff. |
| CHECK(invoke<int>(*code, &cond - 2, (sizeof(uint32_t) * 2) >> logScale) == 1); |
| cond = 0x00000000U; // All bytes are 0. |
| CHECK(invoke<int>(*code, &cond - 2, (sizeof(uint32_t) * 2) >> logScale) == 0); |
| } |
| |
| void testComplex(unsigned numVars, unsigned numConstructs) |
| { |
| MonotonicTime before = MonotonicTime::now(); |
| |
| Procedure proc; |
| BasicBlock* current = proc.addBlock(); |
| |
| Const32Value* one = current->appendNew<Const32Value>(proc, Origin(), 1); |
| |
| Vector<int32_t> varSlots; |
| for (unsigned i = numVars; i--;) |
| varSlots.append(i); |
| |
| Vector<Value*> vars; |
| for (int32_t& varSlot : varSlots) { |
| Value* varSlotPtr = current->appendNew<ConstPtrValue>(proc, Origin(), &varSlot); |
| vars.append(current->appendNew<MemoryValue>(proc, Load, Int32, Origin(), varSlotPtr)); |
| } |
| |
| for (unsigned i = 0; i < numConstructs; ++i) { |
| if (i & 1) { |
| // Control flow diamond. |
| unsigned predicateVarIndex = ((i >> 1) + 2) % numVars; |
| unsigned thenIncVarIndex = ((i >> 1) + 0) % numVars; |
| unsigned elseIncVarIndex = ((i >> 1) + 1) % numVars; |
| |
| BasicBlock* thenBlock = proc.addBlock(); |
| BasicBlock* elseBlock = proc.addBlock(); |
| BasicBlock* continuation = proc.addBlock(); |
| |
| current->appendNewControlValue( |
| proc, Branch, Origin(), vars[predicateVarIndex], |
| FrequentedBlock(thenBlock), FrequentedBlock(elseBlock)); |
| |
| UpsilonValue* thenThenResult = thenBlock->appendNew<UpsilonValue>( |
| proc, Origin(), |
| thenBlock->appendNew<Value>(proc, Add, Origin(), vars[thenIncVarIndex], one)); |
| UpsilonValue* thenElseResult = thenBlock->appendNew<UpsilonValue>( |
| proc, Origin(), vars[elseIncVarIndex]); |
| thenBlock->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(continuation)); |
| |
| UpsilonValue* elseElseResult = elseBlock->appendNew<UpsilonValue>( |
| proc, Origin(), |
| elseBlock->appendNew<Value>(proc, Add, Origin(), vars[elseIncVarIndex], one)); |
| UpsilonValue* elseThenResult = elseBlock->appendNew<UpsilonValue>( |
| proc, Origin(), vars[thenIncVarIndex]); |
| elseBlock->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(continuation)); |
| |
| Value* thenPhi = continuation->appendNew<Value>(proc, Phi, Int32, Origin()); |
| thenThenResult->setPhi(thenPhi); |
| elseThenResult->setPhi(thenPhi); |
| vars[thenIncVarIndex] = thenPhi; |
| |
| Value* elsePhi = continuation->appendNew<Value>(proc, Phi, Int32, Origin()); |
| thenElseResult->setPhi(elsePhi); |
| elseElseResult->setPhi(elsePhi); |
| vars[elseIncVarIndex] = thenPhi; |
| |
| current = continuation; |
| } else { |
| // Loop. |
| |
| BasicBlock* loopEntry = proc.addBlock(); |
| BasicBlock* loopReentry = proc.addBlock(); |
| BasicBlock* loopBody = proc.addBlock(); |
| BasicBlock* loopExit = proc.addBlock(); |
| BasicBlock* loopSkip = proc.addBlock(); |
| BasicBlock* continuation = proc.addBlock(); |
| |
| Value* startIndex = vars[((i >> 1) + 1) % numVars]; |
| Value* startSum = current->appendNew<Const32Value>(proc, Origin(), 0); |
| current->appendNewControlValue( |
| proc, Branch, Origin(), startIndex, |
| FrequentedBlock(loopEntry), FrequentedBlock(loopSkip)); |
| |
| UpsilonValue* startIndexForBody = loopEntry->appendNew<UpsilonValue>( |
| proc, Origin(), startIndex); |
| UpsilonValue* startSumForBody = loopEntry->appendNew<UpsilonValue>( |
| proc, Origin(), startSum); |
| loopEntry->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopBody)); |
| |
| Value* bodyIndex = loopBody->appendNew<Value>(proc, Phi, Int32, Origin()); |
| startIndexForBody->setPhi(bodyIndex); |
| Value* bodySum = loopBody->appendNew<Value>(proc, Phi, Int32, Origin()); |
| startSumForBody->setPhi(bodySum); |
| Value* newBodyIndex = loopBody->appendNew<Value>(proc, Sub, Origin(), bodyIndex, one); |
| Value* newBodySum = loopBody->appendNew<Value>( |
| proc, Add, Origin(), |
| bodySum, |
| loopBody->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| loopBody->appendNew<Value>( |
| proc, Add, Origin(), |
| loopBody->appendNew<ConstPtrValue>(proc, Origin(), varSlots.data()), |
| loopBody->appendNew<Value>( |
| proc, Shl, Origin(), |
| loopBody->appendNew<Value>( |
| proc, ZExt32, Origin(), |
| loopBody->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| newBodyIndex, |
| loopBody->appendNew<Const32Value>( |
| proc, Origin(), numVars - 1))), |
| loopBody->appendNew<Const32Value>(proc, Origin(), 2))))); |
| loopBody->appendNewControlValue( |
| proc, Branch, Origin(), newBodyIndex, |
| FrequentedBlock(loopReentry), FrequentedBlock(loopExit)); |
| |
| loopReentry->appendNew<UpsilonValue>(proc, Origin(), newBodyIndex, bodyIndex); |
| loopReentry->appendNew<UpsilonValue>(proc, Origin(), newBodySum, bodySum); |
| loopReentry->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopBody)); |
| |
| UpsilonValue* exitSum = loopExit->appendNew<UpsilonValue>(proc, Origin(), newBodySum); |
| loopExit->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(continuation)); |
| |
| UpsilonValue* skipSum = loopSkip->appendNew<UpsilonValue>(proc, Origin(), startSum); |
| loopSkip->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(continuation)); |
| |
| Value* finalSum = continuation->appendNew<Value>(proc, Phi, Int32, Origin()); |
| exitSum->setPhi(finalSum); |
| skipSum->setPhi(finalSum); |
| |
| current = continuation; |
| vars[((i >> 1) + 0) % numVars] = finalSum; |
| } |
| } |
| |
| current->appendNewControlValue(proc, Return, Origin(), vars[0]); |
| |
| compileProc(proc); |
| |
| MonotonicTime after = MonotonicTime::now(); |
| dataLog(toCString(" That took ", (after - before).milliseconds(), " ms.\n")); |
| } |
| |
| void testBranchBitTest32TmpImm(uint32_t value, uint32_t imm) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* testValue = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* bitOffset = root->appendNew<Const32Value>(proc, Origin(), imm); |
| |
| Value* one = root->appendNew<Const32Value>(proc, Origin(), 1); |
| Value* bitTest = root->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| root->appendNew<Value>(proc, SShr, Origin(), testValue, bitOffset), |
| one); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| bitTest, |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK_EQ(invoke<uint32_t>(*code, value), (value>>(imm%32))&1); |
| } |
| |
| void testBranchBitTest32AddrImm(uint32_t value, uint32_t imm) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* testValue = root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* bitOffset = root->appendNew<Const32Value>(proc, Origin(), imm); |
| |
| Value* one = root->appendNew<Const32Value>(proc, Origin(), 1); |
| Value* bitTest = root->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| root->appendNew<Value>(proc, SShr, Origin(), testValue, bitOffset), |
| one); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| bitTest, |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK_EQ(invoke<uint32_t>(*code, &value), (value>>(imm%32))&1); |
| } |
| |
| void testBranchBitTest32TmpTmp(uint32_t value, uint32_t value2) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* testValue = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* bitOffset = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| |
| Value* one = root->appendNew<Const32Value>(proc, Origin(), 1); |
| Value* bitTest = root->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| root->appendNew<Value>(proc, SShr, Origin(), testValue, bitOffset), |
| one); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| bitTest, |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const32Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const32Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK_EQ(invoke<uint32_t>(*code, value, value2), (value>>(value2%32))&1); |
| } |
| |
| void testBranchBitTest64TmpTmp(uint64_t value, uint64_t value2) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* testValue = root->appendNew<Value>(proc, BitXor, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0), |
| root->appendNew<Const64Value>(proc, Origin(), -1l)); |
| Value* bitOffset = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| |
| Value* one = root->appendNew<Const64Value>(proc, Origin(), 1); |
| Value* bitTest = root->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| testValue, |
| root->appendNew<Value>(proc, Shl, Origin(), one, bitOffset)); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| bitTest, |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const64Value>(proc, Origin(), 0)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const64Value>(proc, Origin(), 1)); |
| |
| auto code = compileProc(proc); |
| CHECK_EQ(invoke<uint64_t>(*code, value, value2), (value>>(value2%64))&1); |
| } |
| |
| void testBranchBitTest64AddrTmp(uint64_t value, uint64_t value2) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* testValue = root->appendNew<MemoryValue>( |
| proc, Load, Int64, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* bitOffset = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| |
| Value* one = root->appendNew<Const64Value>(proc, Origin(), 1); |
| Value* bitTest = root->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| testValue, |
| root->appendNew<Value>(proc, Shl, Origin(), one, bitOffset)); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| bitTest, |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const64Value>(proc, Origin(), 1)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const64Value>(proc, Origin(), 0)); |
| |
| auto code = compileProc(proc); |
| CHECK_EQ(invoke<uint64_t>(*code, &value, value2), (value>>(value2%64))&1); |
| } |
| |
| void testBranchBitTestNegation(uint64_t value, uint64_t value2) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* testValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* bitOffset = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| Value* shift = root->appendNew<Value>(proc, SShr, Origin(), testValue, bitOffset); |
| |
| Value* one = root->appendNew<Const64Value>(proc, Origin(), 1); |
| Value* bitTest = root->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| root->appendNew<Value>(proc, BitXor, Origin(), shift, root->appendNew<Const64Value>(proc, Origin(), -1l)), |
| one); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| bitTest, |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const64Value>(proc, Origin(), 0)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const64Value>(proc, Origin(), 1)); |
| |
| auto code = compileProc(proc); |
| CHECK_EQ(invoke<uint64_t>(*code, value, value2), (value>>(value2%64))&1); |
| } |
| |
| void testBranchBitTestNegation2(uint64_t value, uint64_t value2) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| BasicBlock* thenCase = proc.addBlock(); |
| BasicBlock* elseCase = proc.addBlock(); |
| |
| Value* testValue = root->appendNew<Value>(proc, BitXor, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0), |
| root->appendNew<Const64Value>(proc, Origin(), -1l)); |
| Value* bitOffset = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); |
| Value* shift = root->appendNew<Value>(proc, SShr, Origin(), testValue, bitOffset); |
| |
| Value* one = root->appendNew<Const64Value>(proc, Origin(), 1); |
| Value* bitTest = root->appendNew<Value>( |
| proc, BitAnd, Origin(), |
| shift, |
| one); |
| |
| root->appendNewControlValue( |
| proc, Branch, Origin(), |
| bitTest, |
| FrequentedBlock(thenCase), FrequentedBlock(elseCase)); |
| |
| thenCase->appendNewControlValue( |
| proc, Return, Origin(), |
| thenCase->appendNew<Const64Value>(proc, Origin(), 0)); |
| |
| elseCase->appendNewControlValue( |
| proc, Return, Origin(), |
| elseCase->appendNew<Const64Value>(proc, Origin(), 1)); |
| |
| auto code = compileProc(proc); |
| CHECK_EQ(invoke<uint64_t>(*code, value, value2), (value>>(value2%64))&1); |
| } |
| |
| void testSimplePatchpoint() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(ConstrainedValue(arg1, ValueRep::SomeRegister)); |
| patchpoint->append(ConstrainedValue(arg2, ValueRep::SomeRegister)); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isGPR()); |
| CHECK(params[2].isGPR()); |
| add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr()); |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| } |
| |
| void testSimplePatchpointWithoutOuputClobbersGPArgs() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| Value* const1 = root->appendNew<Const64Value>(proc, Origin(), 42); |
| Value* const2 = root->appendNew<Const64Value>(proc, Origin(), 13); |
| |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Void, Origin()); |
| patchpoint->clobberLate(RegisterSet(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1)); |
| patchpoint->append(ConstrainedValue(const1, ValueRep::SomeRegister)); |
| patchpoint->append(ConstrainedValue(const2, ValueRep::SomeRegister)); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 2); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isGPR()); |
| jit.move(CCallHelpers::TrustedImm32(0x00ff00ff), params[0].gpr()); |
| jit.move(CCallHelpers::TrustedImm32(0x00ff00ff), params[1].gpr()); |
| jit.move(CCallHelpers::TrustedImm32(0x00ff00ff), GPRInfo::argumentGPR0); |
| jit.move(CCallHelpers::TrustedImm32(0x00ff00ff), GPRInfo::argumentGPR1); |
| }); |
| |
| Value* result = root->appendNew<Value>(proc, Add, Origin(), arg1, arg2); |
| root->appendNewControlValue(proc, Return, Origin(), result); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| } |
| |
| void testSimplePatchpointWithOuputClobbersGPArgs() |
| { |
| // We can't predict where the output will be but we want to be sure it is not |
| // one of the clobbered registers which is a bit hard to test. |
| // |
| // What we do is force the hand of our register allocator by clobbering absolutely |
| // everything but 1. The only valid allocation is to give it to the result and |
| // spill everything else. |
| |
| Procedure proc; |
| if (proc.optLevel() < 1) { |
| // FIXME: Air O0 allocator can't handle such programs. We rely on WasmAirIRGenerator |
| // to not use any such constructs where the register allocator is cornered in such |
| // a way. |
| // https://bugs.webkit.org/show_bug.cgi?id=194633 |
| return; |
| } |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| Value* const1 = root->appendNew<Const64Value>(proc, Origin(), 42); |
| Value* const2 = root->appendNew<Const64Value>(proc, Origin(), 13); |
| |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int64, Origin()); |
| |
| RegisterSet clobberAll = RegisterSet::allGPRs(); |
| clobberAll.exclude(RegisterSet::stackRegisters()); |
| clobberAll.exclude(RegisterSet::reservedHardwareRegisters()); |
| clobberAll.clear(GPRInfo::argumentGPR2); |
| patchpoint->clobberLate(clobberAll); |
| |
| patchpoint->append(ConstrainedValue(const1, ValueRep::SomeRegister)); |
| patchpoint->append(ConstrainedValue(const2, ValueRep::SomeRegister)); |
| |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isGPR()); |
| CHECK(params[2].isGPR()); |
| jit.move(params[1].gpr(), params[0].gpr()); |
| jit.add64(params[2].gpr(), params[0].gpr()); |
| |
| clobberAll.forEach([&] (Reg reg) { |
| jit.move(CCallHelpers::TrustedImm32(0x00ff00ff), reg.gpr()); |
| }); |
| }); |
| |
| Value* result = root->appendNew<Value>(proc, Add, Origin(), patchpoint, |
| root->appendNew<Value>(proc, Add, Origin(), arg1, arg2)); |
| root->appendNewControlValue(proc, Return, Origin(), result); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 58); |
| } |
| |
| void testSimplePatchpointWithoutOuputClobbersFPArgs() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1); |
| Value* const1 = root->appendNew<ConstDoubleValue>(proc, Origin(), 42.5); |
| Value* const2 = root->appendNew<ConstDoubleValue>(proc, Origin(), 13.1); |
| |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Void, Origin()); |
| patchpoint->clobberLate(RegisterSet(FPRInfo::argumentFPR0, FPRInfo::argumentFPR1)); |
| patchpoint->append(ConstrainedValue(const1, ValueRep::SomeRegister)); |
| patchpoint->append(ConstrainedValue(const2, ValueRep::SomeRegister)); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 2); |
| CHECK(params[0].isFPR()); |
| CHECK(params[1].isFPR()); |
| jit.moveZeroToDouble(params[0].fpr()); |
| jit.moveZeroToDouble(params[1].fpr()); |
| jit.moveZeroToDouble(FPRInfo::argumentFPR0); |
| jit.moveZeroToDouble(FPRInfo::argumentFPR1); |
| }); |
| |
| Value* result = root->appendNew<Value>(proc, Add, Origin(), arg1, arg2); |
| root->appendNewControlValue(proc, Return, Origin(), result); |
| |
| CHECK(compileAndRun<double>(proc, 1.5, 2.5) == 4); |
| } |
| |
| void testSimplePatchpointWithOuputClobbersFPArgs() |
| { |
| Procedure proc; |
| if (proc.optLevel() < 1) { |
| // FIXME: Air O0 allocator can't handle such programs. We rely on WasmAirIRGenerator |
| // to not use any such constructs where the register allocator is cornered in such |
| // a way. |
| // https://bugs.webkit.org/show_bug.cgi?id=194633 |
| return; |
| } |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1); |
| Value* const1 = root->appendNew<ConstDoubleValue>(proc, Origin(), 42.5); |
| Value* const2 = root->appendNew<ConstDoubleValue>(proc, Origin(), 13.1); |
| |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Double, Origin()); |
| |
| RegisterSet clobberAll = RegisterSet::allFPRs(); |
| clobberAll.exclude(RegisterSet::stackRegisters()); |
| clobberAll.exclude(RegisterSet::reservedHardwareRegisters()); |
| clobberAll.clear(FPRInfo::argumentFPR2); |
| patchpoint->clobberLate(clobberAll); |
| |
| patchpoint->append(ConstrainedValue(const1, ValueRep::SomeRegister)); |
| patchpoint->append(ConstrainedValue(const2, ValueRep::SomeRegister)); |
| |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 3); |
| CHECK(params[0].isFPR()); |
| CHECK(params[1].isFPR()); |
| CHECK(params[2].isFPR()); |
| jit.addDouble(params[1].fpr(), params[2].fpr(), params[0].fpr()); |
| |
| clobberAll.forEach([&] (Reg reg) { |
| jit.moveZeroToDouble(reg.fpr()); |
| }); |
| }); |
| |
| Value* result = root->appendNew<Value>(proc, Add, Origin(), patchpoint, |
| root->appendNew<Value>(proc, Add, Origin(), arg1, arg2)); |
| root->appendNewControlValue(proc, Return, Origin(), result); |
| |
| CHECK(compileAndRun<double>(proc, 1.5, 2.5) == 59.6); |
| } |
| |
| void testPatchpointWithEarlyClobber() |
| { |
| auto test = [] (GPRReg registerToClobber, bool arg1InArgGPR, bool arg2InArgGPR) { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(ConstrainedValue(arg1, ValueRep::SomeRegister)); |
| patchpoint->append(ConstrainedValue(arg2, ValueRep::SomeRegister)); |
| patchpoint->clobberEarly(RegisterSet(registerToClobber)); |
| unsigned optLevel = proc.optLevel(); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| if (optLevel > 1) { |
| CHECK((params[1].gpr() == GPRInfo::argumentGPR0) == arg1InArgGPR); |
| CHECK((params[2].gpr() == GPRInfo::argumentGPR1) == arg2InArgGPR); |
| } |
| |
| add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr()); |
| }); |
| |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| }; |
| |
| test(GPRInfo::nonArgGPR0, true, true); |
| test(GPRInfo::argumentGPR0, false, true); |
| test(GPRInfo::argumentGPR1, true, false); |
| } |
| |
| void testPatchpointCallArg() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(ConstrainedValue(arg1, ValueRep::stackArgument(0))); |
| patchpoint->append(ConstrainedValue(arg2, ValueRep::stackArgument(8))); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isStack()); |
| CHECK(params[2].isStack()); |
| jit.load32( |
| CCallHelpers::Address(GPRInfo::callFrameRegister, params[1].offsetFromFP()), |
| params[0].gpr()); |
| jit.add32( |
| CCallHelpers::Address(GPRInfo::callFrameRegister, params[2].offsetFromFP()), |
| params[0].gpr()); |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| } |
| |
| void testPatchpointFixedRegister() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(ConstrainedValue(arg1, ValueRep(GPRInfo::regT0))); |
| patchpoint->append(ConstrainedValue(arg2, ValueRep(GPRInfo::regT1))); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1] == ValueRep(GPRInfo::regT0)); |
| CHECK(params[2] == ValueRep(GPRInfo::regT1)); |
| add32(jit, GPRInfo::regT0, GPRInfo::regT1, params[0].gpr()); |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| } |
| |
| void testPatchpointAny(ValueRep rep) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(ConstrainedValue(arg1, rep)); |
| patchpoint->append(ConstrainedValue(arg2, rep)); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| // We shouldn't have spilled the inputs, so we assert that they're in registers. |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isGPR()); |
| CHECK(params[2].isGPR()); |
| add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr()); |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| } |
| |
| void testPatchpointGPScratch() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(arg1, ValueRep::SomeRegister); |
| patchpoint->append(arg2, ValueRep::SomeRegister); |
| patchpoint->numGPScratchRegisters = 2; |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| // We shouldn't have spilled the inputs, so we assert that they're in registers. |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isGPR()); |
| CHECK(params[2].isGPR()); |
| CHECK(params.gpScratch(0) != InvalidGPRReg); |
| CHECK(params.gpScratch(0) != params[0].gpr()); |
| CHECK(params.gpScratch(0) != params[1].gpr()); |
| CHECK(params.gpScratch(0) != params[2].gpr()); |
| CHECK(params.gpScratch(1) != InvalidGPRReg); |
| CHECK(params.gpScratch(1) != params.gpScratch(0)); |
| CHECK(params.gpScratch(1) != params[0].gpr()); |
| CHECK(params.gpScratch(1) != params[1].gpr()); |
| CHECK(params.gpScratch(1) != params[2].gpr()); |
| CHECK(!params.unavailableRegisters().get(params.gpScratch(0))); |
| CHECK(!params.unavailableRegisters().get(params.gpScratch(1))); |
| add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr()); |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| } |
| |
| void testPatchpointFPScratch() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); |
| Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(arg1, ValueRep::SomeRegister); |
| patchpoint->append(arg2, ValueRep::SomeRegister); |
| patchpoint->numFPScratchRegisters = 2; |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| // We shouldn't have spilled the inputs, so we assert that they're in registers. |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isGPR()); |
| CHECK(params[2].isGPR()); |
| CHECK(params.fpScratch(0) != InvalidFPRReg); |
| CHECK(params.fpScratch(1) != InvalidFPRReg); |
| CHECK(params.fpScratch(1) != params.fpScratch(0)); |
| CHECK(!params.unavailableRegisters().get(params.fpScratch(0))); |
| CHECK(!params.unavailableRegisters().get(params.fpScratch(1))); |
| add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr()); |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1, 2) == 3); |
| } |
| |
| void testPatchpointLotsOfLateAnys() |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Vector<int> things; |
| for (unsigned i = 200; i--;) |
| things.append(i); |
| |
| Vector<Value*> values; |
| for (int& thing : things) { |
| Value* value = root->appendNew<MemoryValue>( |
| proc, Load, Int32, Origin(), |
| root->appendNew<ConstPtrValue>(proc, Origin(), &thing)); |
| values.append(value); |
| } |
| |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->clobber(RegisterSet::macroScratchRegisters()); |
| for (Value* value : values) |
| patchpoint->append(ConstrainedValue(value, ValueRep::LateColdAny)); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| // We shouldn't have spilled the inputs, so we assert that they're in registers. |
| CHECK(params.size() == things.size() + 1); |
| CHECK(params[0].isGPR()); |
| jit.move(CCallHelpers::TrustedImm32(0), params[0].gpr()); |
| for (unsigned i = 1; i < params.size(); ++i) { |
| if (params[i].isGPR()) { |
| CHECK(params[i] != params[0]); |
| jit.add32(params[i].gpr(), params[0].gpr()); |
| } else { |
| CHECK(params[i].isStack()); |
| jit.add32(CCallHelpers::Address(GPRInfo::callFrameRegister, params[i].offsetFromFP()), params[0].gpr()); |
| } |
| } |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(static_cast<size_t>(compileAndRun<int>(proc)) == (things.size() * (things.size() - 1)) / 2); |
| } |
| |
| void testPatchpointAnyImm(ValueRep rep) |
| { |
| Procedure proc; |
| BasicBlock* root = proc.addBlock(); |
| Value* arg1 = root->appendNew<Value>( |
| proc, Trunc, Origin(), |
| root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)); |
| Value* arg2 = root->appendNew<Const32Value>(proc, Origin(), 42); |
| PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin()); |
| patchpoint->append(ConstrainedValue(arg1, rep)); |
| patchpoint->append(ConstrainedValue(arg2, rep)); |
| patchpoint->setGenerator( |
| [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
| AllowMacroScratchRegisterUsage allowScratch(jit); |
| CHECK(params.size() == 3); |
| CHECK(params[0].isGPR()); |
| CHECK(params[1].isGPR()); |
| CHECK(params[2].isConstant()); |
| CHECK(params[2].value() == 42); |
| jit.add32( |
| CCallHelpers::TrustedImm32(static_cast<int32_t>(params[2].value())), |
| params[1].gpr(), params[0].gpr()); |
| }); |
| root->appendNewControlValue(proc, Return, Origin(), patchpoint); |
| |
| CHECK(compileAndRun<int>(proc, 1) == 43); |
| } |
| |
| void addSExtTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks) |
| { |
| RUN(testSExt8(0)); |
| RUN(testSExt8(1)); |
| RUN(testSExt8(42)); |
| RUN(testSExt8(-1)); |
| RUN(testSExt8(0xff)); |
| RUN(testSExt8(0x100)); |
| RUN(testSExt8Fold(0)); |
| RUN(testSExt8Fold(1)); |
| RUN(testSExt8Fold(42)); |
| RUN(testSExt8Fold(-1)); |
| RUN(testSExt8Fold(0xff)); |
| RUN(testSExt8Fold(0x100)); |
| RUN(testSExt8SExt8(0)); |
| RUN(testSExt8SExt8(1)); |
| RUN(testSExt8SExt8(42)); |
| RUN(testSExt8SExt8(-1)); |
| RUN(testSExt8SExt8(0xff)); |
| RUN(testSExt8SExt8(0x100)); |
| RUN(testSExt8SExt16(0)); |
| RUN(testSExt8SExt16(1)); |
| RUN(testSExt8SExt16(42)); |
| RUN(testSExt8SExt16(-1)); |
| RUN(testSExt8SExt16(0xff)); |
| RUN(testSExt8SExt16(0x100)); |
| RUN(testSExt8SExt16(0xffff)); |
| RUN(testSExt8SExt16(0x10000)); |
| RUN(testSExt8BitAnd(0, 0)); |
| RUN(testSExt8BitAnd(1, 0)); |
| RUN(testSExt8BitAnd(42, 0)); |
| RUN(testSExt8BitAnd(-1, 0)); |
| RUN(testSExt8BitAnd(0xff, 0)); |
| RUN(testSExt8BitAnd(0x100, 0)); |
| RUN(testSExt8BitAnd(0xffff, 0)); |
| RUN(testSExt8BitAnd(0x10000, 0)); |
| RUN(testSExt8BitAnd(0, 0xf)); |
| RUN(testSExt8BitAnd(1, 0xf)); |
| RUN(testSExt8BitAnd(42, 0xf)); |
| RUN(testSExt8BitAnd(-1, 0xf)); |
| RUN(testSExt8BitAnd(0xff, 0xf)); |
| RUN(testSExt8BitAnd(0x100, 0xf)); |
| RUN(testSExt8BitAnd(0xffff, 0xf)); |
| RUN(testSExt8BitAnd(0x10000, 0xf)); |
| RUN(testSExt8BitAnd(0, 0xff)); |
| RUN(testSExt8BitAnd(1, 0xff)); |
| RUN(testSExt8BitAnd(42, 0xff)); |
| RUN(testSExt8BitAnd(-1, 0xff)); |
| RUN(testSExt8BitAnd(0xff, 0xff)); |
| RUN(testSExt8BitAnd(0x100, 0xff)); |
| RUN(testSExt8BitAnd(0xffff, 0xff)); |
| RUN(testSExt8BitAnd(0x10000, 0xff)); |
| RUN(testSExt8BitAnd(0, 0x80)); |
| RUN(testSExt8BitAnd(1, 0x80)); |
| RUN(testSExt8BitAnd(42, 0x80)); |
| RUN(testSExt8BitAnd(-1, 0x80)); |
| RUN(testSExt8BitAnd(0xff, 0x80)); |
| RUN(testSExt8BitAnd(0x100, 0x80)); |
| RUN(testSExt8BitAnd(0xffff, 0x80)); |
| RUN(testSExt8BitAnd(0x10000, 0x80)); |
| RUN(testBitAndSExt8(0, 0xf)); |
| RUN(testBitAndSExt8(1, 0xf)); |
| RUN(testBitAndSExt8(42, 0xf)); |
| RUN(testBitAndSExt8(-1, 0xf)); |
| RUN(testBitAndSExt8(0xff, 0xf)); |
| RUN(testBitAndSExt8(0x100, 0xf)); |
| RUN(testBitAndSExt8(0xffff, 0xf)); |
| RUN(testBitAndSExt8(0x10000, 0xf)); |
| RUN(testBitAndSExt8(0, 0xff)); |
| RUN(testBitAndSExt8(1, 0xff)); |
| RUN(testBitAndSExt8(42, 0xff)); |
| RUN(testBitAndSExt8(-1, 0xff)); |
| RUN(testBitAndSExt8(0xff, 0xff)); |
| RUN(testBitAndSExt8(0x100, 0xff)); |
| RUN(testBitAndSExt8(0xffff, 0xff)); |
| RUN(testBitAndSExt8(0x10000, 0xff)); |
| RUN(testBitAndSExt8(0, 0xfff)); |
| RUN(testBitAndSExt8(1, 0xfff)); |
| RUN(testBitAndSExt8(42, 0xfff)); |
| RUN(testBitAndSExt8(-1, 0xfff)); |
| RUN(testBitAndSExt8(0xff, 0xfff)); |
| RUN(testBitAndSExt8(0x100, 0xfff)); |
| RUN(testBitAndSExt8(0xffff, 0xfff)); |
| RUN(testBitAndSExt8(0x10000, 0xfff)); |
| |
| RUN(testSExt16(0)); |
| RUN(testSExt16(1)); |
| RUN(testSExt16(42)); |
| RUN(testSExt16(-1)); |
| RUN(testSExt16(0xffff)); |
| RUN(testSExt16(0x10000)); |
| RUN(testSExt16Fold(0)); |
| RUN(testSExt16Fold(1)); |
| RUN(testSExt16Fold(42)); |
| RUN(testSExt16Fold(-1)); |
| RUN(testSExt16Fold(0xffff)); |
| RUN(testSExt16Fold(0x10000)); |
| RUN(testSExt16SExt8(0)); |
| RUN(testSExt16SExt8(1)); |
| RUN(testSExt16SExt8(42)); |
| RUN(testSExt16SExt8(-1)); |
| RUN(testSExt16SExt8(0xffff)); |
| RUN(testSExt16SExt8(0x10000)); |
| RUN(testSExt16SExt16(0)); |
| RUN(testSExt16SExt16(1)); |
| RUN(testSExt16SExt16(42)); |
| RUN(testSExt16SExt16(-1)); |
| RUN(testSExt16SExt16(0xffff)); |
| RUN(testSExt16SExt16(0x10000)); |
| RUN(testSExt16SExt16(0xffffff)); |
| RUN(testSExt16SExt16(0x1000000)); |
| RUN(testSExt16BitAnd(0, 0)); |
| RUN(testSExt16BitAnd(1, 0)); |
| RUN(testSExt16BitAnd(42, 0)); |
| RUN(testSExt16BitAnd(-1, 0)); |
| RUN(testSExt16BitAnd(0xffff, 0)); |
| RUN(testSExt16BitAnd(0x10000, 0)); |
| RUN(testSExt16BitAnd(0xffffff, 0)); |
| RUN(testSExt16BitAnd(0x1000000, 0)); |
| RUN(testSExt16BitAnd(0, 0xf)); |
| RUN(testSExt16BitAnd(1, 0xf)); |
| RUN(testSExt16BitAnd(42, 0xf)); |
| RUN(testSExt16BitAnd(-1, 0xf)); |
| RUN(testSExt16BitAnd(0xffff, 0xf)); |
| RUN(testSExt16BitAnd(0x10000, 0xf)); |
| RUN(testSExt16BitAnd(0xffffff, 0xf)); |
| RUN(testSExt16BitAnd(0x1000000, 0xf)); |
| RUN(testSExt16BitAnd(0, 0xffff)); |
| RUN(testSExt16BitAnd(1, 0xffff)); |
| RUN(testSExt16BitAnd(42, 0xffff)); |
| RUN(testSExt16BitAnd(-1, 0xffff)); |
| RUN(testSExt16BitAnd(0xffff, 0xffff)); |
| RUN(testSExt16BitAnd(0x10000, 0xffff)); |
| RUN(testSExt16BitAnd(0xffffff, 0xffff)); |
| RUN(testSExt16BitAnd(0x1000000, 0xffff)); |
| RUN(testSExt16BitAnd(0, 0x8000)); |
| RUN(testSExt16BitAnd(1, 0x8000)); |
| RUN(testSExt16BitAnd(42, 0x8000)); |
| RUN(testSExt16BitAnd(-1, 0x8000)); |
| RUN(testSExt16BitAnd(0xffff, 0x8000)); |
| RUN(testSExt16BitAnd(0x10000, 0x8000)); |
| RUN(testSExt16BitAnd(0xffffff, 0x8000)); |
| RUN(testSExt16BitAnd(0x1000000, 0x8000)); |
| RUN(testBitAndSExt16(0, 0xf)); |
| RUN(testBitAndSExt16(1, 0xf)); |
| RUN(testBitAndSExt16(42, 0xf)); |
| RUN(testBitAndSExt16(-1, 0xf)); |
| RUN(testBitAndSExt16(0xffff, 0xf)); |
| RUN(testBitAndSExt16(0x10000, 0xf)); |
| RUN(testBitAndSExt16(0xffffff, 0xf)); |
| RUN(testBitAndSExt16(0x1000000, 0xf)); |
| RUN(testBitAndSExt16(0, 0xffff)); |
| RUN(testBitAndSExt16(1, 0xffff)); |
| RUN(testBitAndSExt16(42, 0xffff)); |
| RUN(testBitAndSExt16(-1, 0xffff)); |
| RUN(testBitAndSExt16(0xffff, 0xffff)); |
| RUN(testBitAndSExt16(0x10000, 0xffff)); |
| RUN(testBitAndSExt16(0xffffff, 0xffff)); |
| RUN(testBitAndSExt16(0x1000000, 0xffff)); |
| RUN(testBitAndSExt16(0, 0xfffff)); |
| RUN(testBitAndSExt16(1, 0xfffff)); |
| RUN(testBitAndSExt16(42, 0xfffff)); |
| RUN(testBitAndSExt16(-1, 0xfffff)); |
| RUN(testBitAndSExt16(0xffff, 0xfffff)); |
| RUN(testBitAndSExt16(0x10000, 0xfffff)); |
| RUN(testBitAndSExt16(0xffffff, 0xfffff)); |
| RUN(testBitAndSExt16(0x1000000, 0xfffff)); |
| |
| RUN(testSExt32BitAnd(0, 0)); |
| RUN(testSExt32BitAnd(1, 0)); |
| RUN(testSExt32BitAnd(42, 0)); |
| RUN(testSExt32BitAnd(-1, 0)); |
| RUN(testSExt32BitAnd(0x80000000, 0)); |
| RUN(testSExt32BitAnd(0, 0xf)); |
| RUN(testSExt32BitAnd(1, 0xf)); |
| RUN(testSExt32BitAnd(42, 0xf)); |
| RUN(testSExt32BitAnd(-1, 0xf)); |
| RUN(testSExt32BitAnd(0x80000000, 0xf)); |
| RUN(testSExt32BitAnd(0, 0x80000000)); |
| RUN(testSExt32BitAnd(1, 0x80000000)); |
| RUN(testSExt32BitAnd(42, 0x80000000)); |
| RUN(testSExt32BitAnd(-1, 0x80000000)); |
| RUN(testSExt32BitAnd(0x80000000, 0x80000000)); |
| RUN(testBitAndSExt32(0, 0xf)); |
| RUN(testBitAndSExt32(1, 0xf)); |
| RUN(testBitAndSExt32(42, 0xf)); |
| RUN(testBitAndSExt32(-1, 0xf)); |
| RUN(testBitAndSExt32(0xffff, 0xf)); |
| RUN(testBitAndSExt32(0x10000, 0xf)); |
| RUN(testBitAndSExt32(0xffffff, 0xf)); |
| RUN(testBitAndSExt32(0x1000000, 0xf)); |
| RUN(testBitAndSExt32(0, 0xffff00000000llu)); |
| RUN(testBitAndSExt32(1, 0xffff00000000llu)); |
| RUN(testBitAndSExt32(42, 0xffff00000000llu)); |
| RUN(testBitAndSExt32(-1, 0xffff00000000llu)); |
| RUN(testBitAndSExt32(0x80000000, 0xffff00000000llu)); |
| } |
| |
| #endif // ENABLE(B3_JIT) |