Commit 4d189594 authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

Avoid a call to the runtime system when doing binary fp ops on ARM

(at the moment only if we do not need to allocate a heap number).
Find a few more oportunities to avoid heap number allocation on IA32.
Add some infrastructure to test coverage of generated ARM code in our
tests.
Review URL: http://codereview.chromium.org/67163

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1720 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent b92740bf
......@@ -582,4 +582,40 @@ ExternalReference ExternalReference::debug_step_in_fp_address() {
return ExternalReference(Debug::step_in_fp_addr());
}
static double add_two_doubles(double x, double y) {
return x + y;
}
static double sub_two_doubles(double x, double y) {
return x - y;
}
static double mul_two_doubles(double x, double y) {
return x * y;
}
ExternalReference ExternalReference::double_fp_operation(
Token::Value operation) {
typedef double BinaryFPOperation(double x, double y);
BinaryFPOperation* function = NULL;
switch (operation) {
case Token::ADD:
function = &add_two_doubles;
break;
case Token::SUB:
function = &sub_two_doubles;
break;
case Token::MUL:
function = &mul_two_doubles;
break;
default:
UNREACHABLE();
}
return ExternalReference(FUNCTION_ADDR(function));
}
} } // namespace v8::internal
......@@ -38,6 +38,7 @@
#include "runtime.h"
#include "top.h"
#include "zone-inl.h"
#include "token.h"
namespace v8 { namespace internal {
......@@ -417,6 +418,8 @@ class ExternalReference BASE_EMBEDDED {
// Used to check if single stepping is enabled in generated code.
static ExternalReference debug_step_in_fp_address();
static ExternalReference double_fp_operation(Token::Value operation);
Address address() const {return address_;}
private:
......
......@@ -34,7 +34,7 @@
namespace v8 { namespace internal {
#define __ masm->
#define __ DEFINE_MASM(masm)
void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
......@@ -218,8 +218,9 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
__ mov(r5, Operand(r4));
__ mov(r6, Operand(r4));
__ mov(r7, Operand(r4));
if (kR9Available == 1)
if (kR9Available == 1) {
__ mov(r9, Operand(r4));
}
// Invoke the code and pass argc as r0.
__ mov(r0, Operand(r3));
......
This diff is collapsed.
......@@ -35,9 +35,6 @@ class DeferredCode;
class RegisterAllocator;
class RegisterFile;
// Mode to overwrite BinaryExpression values.
enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
......@@ -292,10 +289,13 @@ class CodeGenerator: public AstVisitor {
void ToBoolean(JumpTarget* true_target, JumpTarget* false_target);
void GenericBinaryOperation(Token::Value op);
void GenericBinaryOperation(Token::Value op, OverwriteMode overwrite_mode);
void Comparison(Condition cc, bool strict = false);
void SmiOperation(Token::Value op, Handle<Object> value, bool reversed);
void SmiOperation(Token::Value op,
Handle<Object> value,
bool reversed,
OverwriteMode mode);
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
......
......@@ -3933,6 +3933,9 @@ void CodeGenerator::VisitAssignment(Assignment* node) {
} else {
Literal* literal = node->value()->AsLiteral();
bool overwrite_value =
(node->value()->AsBinaryOperation() != NULL &&
node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
Variable* right_var = node->value()->AsVariableProxy()->AsVariable();
// There are two cases where the target is not read in the right hand
// side, that are easy to test for: the right hand side is a literal,
......@@ -3945,7 +3948,9 @@ void CodeGenerator::VisitAssignment(Assignment* node) {
target.GetValue(NOT_INSIDE_TYPEOF);
}
Load(node->value());
GenericBinaryOperation(node->binary_op(), node->type());
GenericBinaryOperation(node->binary_op(),
node->type(),
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
}
if (var != NULL &&
......
......@@ -35,9 +35,6 @@ class DeferredCode;
class RegisterAllocator;
class RegisterFile;
// Mode to overwrite BinaryExpression values.
enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
......@@ -435,7 +432,7 @@ class CodeGenerator: public AstVisitor {
void GenericBinaryOperation(
Token::Value op,
SmiAnalysis* type,
const OverwriteMode overwrite_mode = NO_OVERWRITE);
OverwriteMode overwrite_mode);
// If possible, combine two constant smi values using op to produce
// a smi result, and push it on the virtual frame, all at compile time.
......
......@@ -71,6 +71,11 @@
// CodeForStatementPosition
// CodeForSourcePosition
// Mode to overwrite BinaryExpression values.
enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
#ifdef ARM
#include "codegen-arm.h"
#else
......
......@@ -106,7 +106,12 @@ enum SoftwareInterruptCodes {
call_rt_r5 = 0x10,
call_rt_r2 = 0x11,
// break point
break_point = 0x20
break_point = 0x20,
// FP operations. These simulate calling into C for a moment to do fp ops.
// They should trash all caller-save registers.
simulator_fp_add = 0x21,
simulator_fp_sub = 0x22,
simulator_fp_mul = 0x23
};
......
......@@ -58,7 +58,7 @@ bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
}
#define __ masm->
#define __ DEFINE_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
......
......@@ -261,6 +261,15 @@ void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) {
case break_point:
Print("break_point");
return;
case simulator_fp_add:
Print("simulator_fp_add");
return;
case simulator_fp_mul:
Print("simulator_fp_mul");
return;
case simulator_fp_sub:
Print("simulator_fp_sub");
return;
default:
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"%d",
......
......@@ -514,6 +514,16 @@ inline Dest bit_cast(const Source& source) {
}
#ifdef ARM_GENERATED_CODE_COVERAGE
#define CODE_COVERAGE_STRINGIFY(x) #x
#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
#define DEFINE_MASM(masm) masm->stop(__FILE_LINE__); masm->
#else
#define DEFINE_MASM(masm) masm->
#endif
} } // namespace v8::internal
#endif // V8_GLOBALS_H_
......@@ -39,7 +39,7 @@ namespace v8 { namespace internal {
// Static IC stub generators.
//
#define __ masm->
#define __ DEFINE_MASM(masm)
// Helper function used from LoadIC/CallIC GenerateNormal.
......@@ -96,7 +96,9 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
// Compute the masked index: (hash + i + i * i) & mask.
__ ldr(t1, FieldMemOperand(r2, String::kLengthOffset));
__ mov(t1, Operand(t1, LSR, String::kHashShift));
if (i > 0) __ add(t1, t1, Operand(Dictionary::GetProbeOffset(i)));
if (i > 0) {
__ add(t1, t1, Operand(Dictionary::GetProbeOffset(i)));
}
__ and_(t1, t1, Operand(r3));
// Scale the index by multiplying by the element size.
......
......@@ -168,11 +168,11 @@ void MacroAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode,
}
void MacroAssembler::Ret() {
void MacroAssembler::Ret(Condition cond) {
#if USE_BX
bx(lr);
bx(lr, cond);
#else
mov(pc, Operand(lr));
mov(pc, Operand(lr), LeaveCC, cond);
#endif
}
......
......@@ -86,7 +86,7 @@ class MacroAssembler: public Assembler {
void Call(Register target, Condition cond = al);
void Call(byte* target, RelocInfo::Mode rmode, Condition cond = al);
void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
void Ret();
void Ret(Condition cond = al);
// Jumps to the label at the index given by the Smi in "index".
void SmiJumpTable(Register index, Vector<Label*> targets);
......
......@@ -3482,6 +3482,7 @@ static Object* Runtime_NumberToSmi(Arguments args) {
return Heap::nan_value();
}
static Object* Runtime_NumberAdd(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
......
......@@ -683,6 +683,18 @@ void ExternalReferenceTable::PopulateTable() {
UNCLASSIFIED,
10,
"Debug::step_in_fp_addr()");
Add(ExternalReference::double_fp_operation(Token::ADD).address(),
UNCLASSIFIED,
11,
"add_two_doubles");
Add(ExternalReference::double_fp_operation(Token::SUB).address(),
UNCLASSIFIED,
12,
"sub_two_doubles");
Add(ExternalReference::double_fp_operation(Token::MUL).address(),
UNCLASSIFIED,
13,
"mul_two_doubles");
}
......
......@@ -90,12 +90,44 @@ Debugger::~Debugger() {
}
#ifdef ARM_GENERATED_CODE_COVERAGE
static FILE* coverage_log = NULL;
static void InitializeCoverage() {
char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
if (file_name != NULL) {
coverage_log = fopen(file_name, "aw+");
}
}
void Debugger::Stop(Instr* instr) {
char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff);
if (strlen(str) > 0) {
if (coverage_log != NULL) {
fprintf(coverage_log, "Simulator hit %s\n", str);
fflush(coverage_log);
}
instr->SetInstructionBits(0xe1a00000); // Overwrite with nop.
}
sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
}
#else // ndef ARM_GENERATED_CODE_COVERAGE
static void InitializeCoverage() {
}
void Debugger::Stop(Instr* instr) {
const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff);
PrintF("Simulator hit %s\n", str);
sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
Debug();
}
#endif
static const char* reg_names[] = { "r0", "r1", "r2", "r3",
......@@ -375,6 +407,7 @@ Simulator::Simulator() {
// access violation if the simulator ever tries to execute it.
registers_[pc] = bad_lr;
registers_[lr] = bad_lr;
InitializeCoverage();
}
......@@ -427,6 +460,37 @@ int32_t Simulator::get_pc() const {
}
// For use in calls that take two double values, constructed from r0, r1, r2
// and r3.
void Simulator::GetFpArgs(double* x, double* y) {
// We use a char buffer to get around the strict-aliasing rules which
// otherwise allow the compiler to optimize away the copy.
char buffer[2 * sizeof(registers_[0])];
// Registers 0 and 1 -> x.
memcpy(buffer, registers_, sizeof(buffer));
memcpy(x, buffer, sizeof(buffer));
// Registers 2 and 3 -> y.
memcpy(buffer, registers_ + 2, sizeof(buffer));
memcpy(y, buffer, sizeof(buffer));
}
void Simulator::SetFpResult(const double& result) {
char buffer[2 * sizeof(registers_[0])];
memcpy(buffer, &result, sizeof(buffer));
// result -> registers 0 and 1.
memcpy(registers_, buffer, sizeof(buffer));
}
void Simulator::TrashCallerSaveRegisters() {
// We don't trash the registers with the return value.
registers_[2] = 0x50Bad4U;
registers_[3] = 0x50Bad4U;
registers_[12] = 0x50Bad4U;
}
// The ARM cannot do unaligned reads and writes. On some ARM platforms an
// interrupt is caused. On others it does a funky rotation thing. For now we
// simply disallow unaligned reads, but at some point we may want to move to
......@@ -862,7 +926,8 @@ typedef int64_t (*SimulatorRuntimeCall)(intptr_t arg0, intptr_t arg1);
// Software interrupt instructions are used by the simulator to call into the
// C-based V8 runtime.
void Simulator::SoftwareInterrupt(Instr* instr) {
switch (instr->SwiField()) {
int swi = instr->SwiField();
switch (swi) {
case call_rt_r5: {
SimulatorRuntimeCall target =
reinterpret_cast<SimulatorRuntimeCall>(get_register(r5));
......@@ -894,6 +959,30 @@ void Simulator::SoftwareInterrupt(Instr* instr) {
dbg.Debug();
break;
}
{
double x, y, z;
case simulator_fp_add:
GetFpArgs(&x, &y);
z = x + y;
SetFpResult(z);
TrashCallerSaveRegisters();
set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
break;
case simulator_fp_sub:
GetFpArgs(&x, &y);
z = x - y;
SetFpResult(z);
TrashCallerSaveRegisters();
set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
break;
case simulator_fp_mul:
GetFpArgs(&x, &y);
z = x * y;
SetFpResult(z);
TrashCallerSaveRegisters();
set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
break;
}
default: {
UNREACHABLE();
break;
......
......@@ -174,6 +174,12 @@ class Simulator {
// Executes one instruction.
void InstructionDecode(Instr* instr);
// For use in calls that take two double values, constructed from r0, r1, r2
// and r3.
void GetFpArgs(double* x, double* y);
void SetFpResult(const double& result);
void TrashCallerSaveRegisters();
// architecture state
int32_t registers_[16];
bool n_flag_;
......
......@@ -33,7 +33,7 @@
namespace v8 { namespace internal {
#define __ masm->
#define __ DEFINE_MASM(masm)
static void ProbeTable(MacroAssembler* masm,
......@@ -183,7 +183,7 @@ void StubCompiler::GenerateLoadField(MacroAssembler* masm,
// Check that the maps haven't changed.
Register reg =
__ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
GenerateFastPropertyLoad(masm, r0, reg, holder, index);
__ Ret();
}
......@@ -203,7 +203,7 @@ void StubCompiler::GenerateLoadConstant(MacroAssembler* masm,
// Check that the maps haven't changed.
Register reg =
__ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Return the constant value.
__ mov(r0, Operand(Handle<Object>(value)));
......@@ -226,7 +226,7 @@ void StubCompiler::GenerateLoadCallback(MacroAssembler* masm,
// Check that the maps haven't changed.
Register reg =
__ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Push the arguments on the JS stack of the caller.
__ push(receiver); // receiver
......@@ -256,7 +256,7 @@ void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm,
// Check that the maps haven't changed.
Register reg =
__ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Push the arguments on the JS stack of the caller.
__ push(receiver); // receiver
......@@ -456,8 +456,7 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
#undef __
#define __ masm()->
#define __ DEFINE_MASM(masm())
Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
......@@ -511,7 +510,7 @@ Object* CallStubCompiler::CompileCallField(Object* object,
// Do the right check and compute the holder register.
Register reg =
__ CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss);
masm()->CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss);
GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
// Check that the function really is a function.
......
......@@ -36,7 +36,8 @@ namespace v8 { namespace internal {
// -------------------------------------------------------------------------
// VirtualFrame implementation.
#define __ masm_->
#define __ DEFINE_MASM(masm_)
// On entry to a function, the virtual frame already contains the
// receiver and the parameters. All initial frame elements are in
......
......@@ -30,9 +30,9 @@ const SMI_MIN = -(1 << 30);
function testmulneg(a, b) {
var base = a * b;
assertEquals(-base, a * -b);
assertEquals(-base, -a * b);
assertEquals(base, -a * -b);
assertEquals(-base, a * -b, "a * -b where a = " + a + ", b = " + b);
assertEquals(-base, -a * b, "-a * b where a = " + a + ", b = " + b);
assertEquals(base, -a * -b, "*-a * -b where a = " + a + ", b = " + b);
}
testmulneg(2, 3);
......
......@@ -33,10 +33,14 @@ function testLimits() {
var addAboveMax = Number.MAX_VALUE + 1/eps;
var mulBelowMin = Number.MIN_VALUE * (1 - eps);
var addBelowMin = Number.MIN_VALUE - eps;
assertTrue(mulAboveMax == Number.MAX_VALUE || mulAboveMax == Infinity);
assertTrue(addAboveMax == Number.MAX_VALUE || addAboveMax == Infinity);
assertTrue(mulBelowMin == Number.MIN_VALUE || mulBelowMin <= 0);
assertTrue(addBelowMin == Number.MIN_VALUE || addBelowMin <= 0);
assertTrue(mulAboveMax == Number.MAX_VALUE ||
mulAboveMax == Infinity, "mul" + i);
assertTrue(addAboveMax == Number.MAX_VALUE ||
addAboveMax == Infinity, "add" + i);
assertTrue(mulBelowMin == Number.MIN_VALUE ||
mulBelowMin <= 0, "mul2" + i);
assertTrue(addBelowMin == Number.MIN_VALUE ||
addBelowMin <= 0, "add2" + i);
}
}
......
......@@ -100,3 +100,98 @@ assertEquals(SMI_MIN - ONE_HUNDRED, Sub100(SMI_MIN)); // overflow
assertEquals(ONE_HUNDRED - SMI_MIN, Sub100Reversed(SMI_MIN)); // overflow
assertEquals(42 - ONE_HUNDRED, Sub100(OBJ_42)); // non-smi
assertEquals(ONE_HUNDRED - 42, Sub100Reversed(OBJ_42)); // non-smi
function Shr1(x) {
return x >>> 1;
}
function Shr100(x) {
return x >>> 100;
}
function Shr1Reversed(x) {
return 1 >>> x;
}
function Shr100Reversed(x) {
return 100 >>> x;
}
function Sar1(x) {
return x >> 1;
}
function Sar100(x) {
return x >> 100;
}
function Sar1Reversed(x) {
return 1 >> x;
}
function Sar100Reversed(x) {
return 100 >> x;
}
assertEquals(0, Shr1(1));
assertEquals(0, Sar1(1));
assertEquals(0, Shr1Reversed(2));
assertEquals(0, Sar1Reversed(2));
assertEquals(1610612736, Shr1(SMI_MIN));
assertEquals(-536870912, Sar1(SMI_MIN));
assertEquals(1, Shr1Reversed(SMI_MIN));
assertEquals(1, Sar1Reversed(SMI_MIN));
assertEquals(21, Shr1(OBJ_42));
assertEquals(21, Sar1(OBJ_42));
assertEquals(0, Shr1Reversed(OBJ_42));
assertEquals(0, Sar1Reversed(OBJ_42));
assertEquals(6, Shr100(100));
assertEquals(6, Sar100(100));
assertEquals(12, Shr100Reversed(99));
assertEquals(12, Sar100Reversed(99));
assertEquals(201326592, Shr100(SMI_MIN));
assertEquals(-67108864, Sar100(SMI_MIN));
assertEquals(100, Shr100Reversed(SMI_MIN));
assertEquals(100, Sar100Reversed(SMI_MIN));
assertEquals(2, Shr100(OBJ_42));
assertEquals(2, Sar100(OBJ_42));
assertEquals(0, Shr100Reversed(OBJ_42));
assertEquals(0, Sar100Reversed(OBJ_42));
function Xor1(x) {
return x ^ 1;
}
function Xor100(x) {
return x ^ 100;
}
function Xor1Reversed(x) {
return 1 ^ x;
}
function Xor100Reversed(x) {
return 100 ^ x;
}
assertEquals(0, Xor1(1));
assertEquals(3, Xor1Reversed(2));
assertEquals(SMI_MIN + 1, Xor1(SMI_MIN));
assertEquals(SMI_MIN + 1, Xor1Reversed(SMI_MIN));
assertEquals(43, Xor1(OBJ_42));
assertEquals(43, Xor1Reversed(OBJ_42));
assertEquals(0, Xor100(100));
assertEquals(7, Xor100Reversed(99));
assertEquals(-1073741724, Xor100(SMI_MIN));
assertEquals(-1073741724, Xor100Reversed(SMI_MIN));
assertEquals(78, Xor100(OBJ_42));
assertEquals(78, Xor100Reversed(OBJ_42));
var x = 0x23; var y = 0x35;
assertEquals(0x16, x ^ y);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment