2012-05-11 23:43:27 +00:00
|
|
|
/* Copyright (c) 2008-2012, Avian Contributors
|
2008-06-25 20:53:48 +00:00
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software
|
|
|
|
for any purpose with or without fee is hereby granted, provided
|
|
|
|
that the above copyright notice and this permission notice appear
|
|
|
|
in all copies.
|
|
|
|
|
|
|
|
There is NO WARRANTY for this software. See license.txt for
|
|
|
|
details. */
|
|
|
|
|
2012-05-01 19:16:32 +00:00
|
|
|
#include "environment.h"
|
2008-02-08 23:18:57 +00:00
|
|
|
#include "assembler.h"
|
2011-08-30 01:00:17 +00:00
|
|
|
#include "target.h"
|
2008-02-09 20:11:37 +00:00
|
|
|
#include "vector.h"
|
2008-02-08 23:18:57 +00:00
|
|
|
|
2008-10-15 00:45:31 +00:00
|
|
|
#define CAST1(x) reinterpret_cast<UnaryOperationType>(x)
|
|
|
|
#define CAST2(x) reinterpret_cast<BinaryOperationType>(x)
|
2009-10-10 21:03:23 +00:00
|
|
|
#define CAST_BRANCH(x) reinterpret_cast<BranchOperationType>(x)
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2008-02-08 23:18:57 +00:00
|
|
|
using namespace vm;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2009-08-27 00:26:44 +00:00
|
|
|
namespace local {
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
enum {
|
2008-02-08 23:18:57 +00:00
|
|
|
rax = 0,
|
|
|
|
rcx = 1,
|
|
|
|
rdx = 2,
|
|
|
|
rbx = 3,
|
|
|
|
rsp = 4,
|
|
|
|
rbp = 5,
|
|
|
|
rsi = 6,
|
|
|
|
rdi = 7,
|
|
|
|
r8 = 8,
|
|
|
|
r9 = 9,
|
|
|
|
r10 = 10,
|
|
|
|
r11 = 11,
|
|
|
|
r12 = 12,
|
|
|
|
r13 = 13,
|
|
|
|
r14 = 14,
|
|
|
|
r15 = 15,
|
|
|
|
};
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
enum {
|
|
|
|
xmm0 = r15 + 1,
|
|
|
|
xmm1,
|
|
|
|
xmm2,
|
|
|
|
xmm3,
|
|
|
|
xmm4,
|
|
|
|
xmm5,
|
|
|
|
xmm6,
|
|
|
|
xmm7,
|
|
|
|
xmm8,
|
|
|
|
xmm9,
|
|
|
|
xmm10,
|
|
|
|
xmm11,
|
|
|
|
xmm12,
|
|
|
|
xmm13,
|
|
|
|
xmm14,
|
|
|
|
xmm15,
|
|
|
|
};
|
|
|
|
|
2009-09-20 21:43:32 +00:00
|
|
|
const unsigned GeneralRegisterMask
|
2011-08-30 01:00:17 +00:00
|
|
|
= TargetBytesPerWord == 4 ? 0x000000ff : 0x0000ffff;
|
2009-09-20 21:43:32 +00:00
|
|
|
|
|
|
|
const unsigned FloatRegisterMask
|
2011-08-30 01:00:17 +00:00
|
|
|
= TargetBytesPerWord == 4 ? 0x00ff0000 : 0xffff0000;
|
2009-08-06 16:26:22 +00:00
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
const unsigned FrameHeaderSize = (UseFramePointer ? 2 : 1);
|
2008-09-07 20:12:11 +00:00
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
const int LongJumpRegister = r10;
|
|
|
|
|
2009-04-19 22:36:11 +00:00
|
|
|
const unsigned StackAlignmentInBytes = 16;
|
2011-08-30 01:00:17 +00:00
|
|
|
const unsigned StackAlignmentInWords = StackAlignmentInBytes / TargetBytesPerWord;
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bool
|
2011-09-01 03:18:00 +00:00
|
|
|
isInt8(target_intptr_t v)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
|
|
|
return v == static_cast<int8_t>(v);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bool
|
2011-09-01 03:18:00 +00:00
|
|
|
isInt32(target_intptr_t v)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
|
|
|
return v == static_cast<int32_t>(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
class Task;
|
2008-09-08 02:21:52 +00:00
|
|
|
class AlignmentPadding;
|
|
|
|
|
|
|
|
unsigned
|
2009-02-15 00:52:44 +00:00
|
|
|
padding(AlignmentPadding* p, unsigned index, unsigned offset,
|
|
|
|
AlignmentPadding* limit);
|
2008-02-12 00:20:32 +00:00
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
class Context;
|
|
|
|
class MyBlock;
|
|
|
|
|
|
|
|
ResolvedPromise*
|
2011-01-26 00:22:43 +00:00
|
|
|
resolved(Context* c, int64_t value);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
|
2008-09-07 20:12:11 +00:00
|
|
|
class MyBlock: public Assembler::Block {
|
2008-09-07 01:37:12 +00:00
|
|
|
public:
|
2008-09-08 02:21:52 +00:00
|
|
|
MyBlock(unsigned offset):
|
2011-02-01 15:38:59 +00:00
|
|
|
next(0), firstPadding(0), lastPadding(0), offset(offset), start(~0),
|
|
|
|
size(0)
|
2008-09-08 02:21:52 +00:00
|
|
|
{ }
|
2008-09-07 20:12:11 +00:00
|
|
|
|
|
|
|
virtual unsigned resolve(unsigned start, Assembler::Block* next) {
|
|
|
|
this->start = start;
|
|
|
|
this->next = static_cast<MyBlock*>(next);
|
2008-09-08 02:21:52 +00:00
|
|
|
|
2009-02-15 00:52:44 +00:00
|
|
|
return start + size + padding(firstPadding, start, offset, lastPadding);
|
2008-09-07 20:12:11 +00:00
|
|
|
}
|
2008-09-07 01:37:12 +00:00
|
|
|
|
2008-09-08 02:21:52 +00:00
|
|
|
MyBlock* next;
|
|
|
|
AlignmentPadding* firstPadding;
|
|
|
|
AlignmentPadding* lastPadding;
|
2008-09-07 01:37:12 +00:00
|
|
|
unsigned offset;
|
|
|
|
unsigned start;
|
2008-09-07 20:12:11 +00:00
|
|
|
unsigned size;
|
2008-09-07 01:37:12 +00:00
|
|
|
};
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
typedef void (*OperationType)(Context*);
|
|
|
|
|
|
|
|
typedef void (*UnaryOperationType)(Context*, unsigned, Assembler::Operand*);
|
|
|
|
|
|
|
|
typedef void (*BinaryOperationType)
|
|
|
|
(Context*, unsigned, Assembler::Operand*, unsigned, Assembler::Operand*);
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
typedef void (*BranchOperationType)
|
|
|
|
(Context*, TernaryOperation, unsigned, Assembler::Operand*,
|
|
|
|
Assembler::Operand*, Assembler::Operand*);
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
class ArchitectureContext {
|
|
|
|
public:
|
2009-10-10 23:46:43 +00:00
|
|
|
ArchitectureContext(System* s, bool useNativeFeatures):
|
|
|
|
s(s), useNativeFeatures(useNativeFeatures)
|
|
|
|
{ }
|
2008-08-19 23:38:37 +00:00
|
|
|
|
|
|
|
System* s;
|
2009-10-10 23:46:43 +00:00
|
|
|
bool useNativeFeatures;
|
2008-08-19 23:38:37 +00:00
|
|
|
OperationType operations[OperationCount];
|
|
|
|
UnaryOperationType unaryOperations[UnaryOperationCount
|
|
|
|
* OperandTypeCount];
|
2008-09-06 21:25:41 +00:00
|
|
|
BinaryOperationType binaryOperations
|
2009-10-10 21:03:23 +00:00
|
|
|
[(BinaryOperationCount + NonBranchTernaryOperationCount)
|
|
|
|
* OperandTypeCount
|
|
|
|
* OperandTypeCount];
|
|
|
|
BranchOperationType branchOperations
|
|
|
|
[(BranchOperationCount)
|
2008-09-06 21:25:41 +00:00
|
|
|
* OperandTypeCount
|
|
|
|
* OperandTypeCount];
|
2008-08-19 23:38:37 +00:00
|
|
|
};
|
|
|
|
|
2009-11-30 15:38:16 +00:00
|
|
|
class Context {
|
|
|
|
public:
|
|
|
|
Context(System* s, Allocator* a, Zone* zone, ArchitectureContext* ac):
|
|
|
|
s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0),
|
2012-05-08 22:13:17 +00:00
|
|
|
firstBlock(new(zone) MyBlock(0)),
|
2011-02-01 15:38:59 +00:00
|
|
|
lastBlock(firstBlock), ac(ac)
|
2009-11-30 15:38:16 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
Zone* zone;
|
|
|
|
Assembler::Client* client;
|
|
|
|
Vector code;
|
|
|
|
Task* tasks;
|
|
|
|
uint8_t* result;
|
|
|
|
MyBlock* firstBlock;
|
|
|
|
MyBlock* lastBlock;
|
|
|
|
ArchitectureContext* ac;
|
|
|
|
};
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void NO_RETURN
|
2008-02-12 00:20:32 +00:00
|
|
|
abort(Context* c)
|
|
|
|
{
|
|
|
|
abort(c->s);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void NO_RETURN
|
2008-08-19 23:38:37 +00:00
|
|
|
abort(ArchitectureContext* c)
|
|
|
|
{
|
|
|
|
abort(c->s);
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
#ifndef NDEBUG
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
2008-02-12 00:20:32 +00:00
|
|
|
assert(Context* c, bool v)
|
|
|
|
{
|
|
|
|
assert(c->s, v);
|
|
|
|
}
|
2008-08-19 23:38:37 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
2008-08-19 23:38:37 +00:00
|
|
|
assert(ArchitectureContext* c, bool v)
|
|
|
|
{
|
|
|
|
assert(c->s, v);
|
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
#endif // not NDEBUG
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
2008-02-12 00:20:32 +00:00
|
|
|
expect(Context* c, bool v)
|
|
|
|
{
|
|
|
|
expect(c->s, v);
|
|
|
|
}
|
|
|
|
|
2008-04-28 22:08:31 +00:00
|
|
|
ResolvedPromise*
|
|
|
|
resolved(Context* c, int64_t value)
|
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
return new(c->zone) ResolvedPromise(value);
|
2008-04-28 22:08:31 +00:00
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
class CodePromise: public Promise {
|
|
|
|
public:
|
|
|
|
CodePromise(Context* c, unsigned offset): c(c), offset(offset) { }
|
|
|
|
|
|
|
|
virtual int64_t value() {
|
|
|
|
if (resolved()) {
|
|
|
|
return reinterpret_cast<intptr_t>(c->result + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
abort(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool resolved() {
|
|
|
|
return c->result != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Context* c;
|
|
|
|
unsigned offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
CodePromise*
|
|
|
|
codePromise(Context* c, unsigned offset)
|
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
return new (c->zone) CodePromise(c, offset);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
class Offset: public Promise {
|
|
|
|
public:
|
2009-02-15 00:52:44 +00:00
|
|
|
Offset(Context* c, MyBlock* block, unsigned offset, AlignmentPadding* limit):
|
2011-07-17 01:12:45 +00:00
|
|
|
c(c), block(block), offset(offset), limit(limit), value_(-1)
|
2008-09-09 00:31:19 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual bool resolved() {
|
|
|
|
return block->start != static_cast<unsigned>(~0);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual int64_t value() {
|
|
|
|
assert(c, resolved());
|
|
|
|
|
2011-07-17 01:12:45 +00:00
|
|
|
if (value_ == -1) {
|
|
|
|
value_ = block->start + (offset - block->offset)
|
|
|
|
+ padding(block->firstPadding, block->start, block->offset, limit);
|
|
|
|
}
|
|
|
|
|
|
|
|
return value_;
|
2008-09-09 00:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Context* c;
|
|
|
|
MyBlock* block;
|
|
|
|
unsigned offset;
|
2009-02-15 00:52:44 +00:00
|
|
|
AlignmentPadding* limit;
|
2011-07-17 01:12:45 +00:00
|
|
|
int value_;
|
2008-09-09 00:31:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Promise*
|
|
|
|
offset(Context* c)
|
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
return new(c->zone) Offset(c, c->lastBlock, c->code.length(), c->lastBlock->lastPadding);
|
2008-09-09 00:31:19 +00:00
|
|
|
}
|
|
|
|
|
2008-02-08 23:18:57 +00:00
|
|
|
class Task {
|
|
|
|
public:
|
2008-02-09 20:11:37 +00:00
|
|
|
Task(Task* next): next(next) { }
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
virtual void run(Context* c) = 0;
|
2008-02-09 20:11:37 +00:00
|
|
|
|
|
|
|
Task* next;
|
2008-02-08 23:18:57 +00:00
|
|
|
};
|
|
|
|
|
2008-11-29 23:08:14 +00:00
|
|
|
void*
|
2008-11-27 20:59:40 +00:00
|
|
|
resolveOffset(System* s, uint8_t* instruction, unsigned instructionSize,
|
|
|
|
int64_t value)
|
|
|
|
{
|
|
|
|
intptr_t v = reinterpret_cast<uint8_t*>(value)
|
|
|
|
- instruction - instructionSize;
|
|
|
|
|
|
|
|
expect(s, isInt32(v));
|
|
|
|
|
|
|
|
int32_t v4 = v;
|
|
|
|
memcpy(instruction + instructionSize - 4, &v4, 4);
|
2008-12-02 02:38:00 +00:00
|
|
|
return instruction + instructionSize;
|
2008-11-27 20:59:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class OffsetListener: public Promise::Listener {
|
|
|
|
public:
|
|
|
|
OffsetListener(System* s, uint8_t* instruction,
|
|
|
|
unsigned instructionSize):
|
|
|
|
s(s),
|
|
|
|
instruction(instruction),
|
|
|
|
instructionSize(instructionSize)
|
|
|
|
{ }
|
|
|
|
|
2009-03-11 01:08:16 +00:00
|
|
|
virtual bool resolve(int64_t value, void** location) {
|
|
|
|
void* p = resolveOffset(s, instruction, instructionSize, value);
|
|
|
|
if (location) *location = p;
|
|
|
|
return false;
|
2008-11-27 20:59:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
uint8_t* instruction;
|
|
|
|
unsigned instructionSize;
|
|
|
|
};
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
class OffsetTask: public Task {
|
|
|
|
public:
|
2008-09-09 00:31:19 +00:00
|
|
|
OffsetTask(Task* next, Promise* promise, Promise* instructionOffset,
|
2008-02-12 00:20:32 +00:00
|
|
|
unsigned instructionSize):
|
|
|
|
Task(next),
|
|
|
|
promise(promise),
|
|
|
|
instructionOffset(instructionOffset),
|
|
|
|
instructionSize(instructionSize)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void run(Context* c) {
|
2008-11-27 20:59:40 +00:00
|
|
|
if (promise->resolved()) {
|
|
|
|
resolveOffset
|
2009-02-09 23:22:01 +00:00
|
|
|
(c->s, c->result + instructionOffset->value(), instructionSize,
|
2008-11-27 20:59:40 +00:00
|
|
|
promise->value());
|
|
|
|
} else {
|
|
|
|
new (promise->listen(sizeof(OffsetListener)))
|
2009-02-09 23:22:01 +00:00
|
|
|
OffsetListener(c->s, c->result + instructionOffset->value(),
|
|
|
|
instructionSize);
|
2008-11-27 20:59:40 +00:00
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Promise* promise;
|
2008-09-09 00:31:19 +00:00
|
|
|
Promise* instructionOffset;
|
2008-02-12 00:20:32 +00:00
|
|
|
unsigned instructionSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
2008-09-09 00:31:19 +00:00
|
|
|
appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset,
|
2008-02-12 00:20:32 +00:00
|
|
|
unsigned instructionSize)
|
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
OffsetTask* task =
|
|
|
|
new(c->zone) OffsetTask(c->tasks, promise, instructionOffset, instructionSize);
|
2011-09-20 22:30:30 +00:00
|
|
|
|
|
|
|
c->tasks = task;
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-11-27 20:59:40 +00:00
|
|
|
void
|
2008-11-28 04:58:32 +00:00
|
|
|
copy(System* s, void* dst, int64_t src, unsigned size)
|
2008-11-27 20:59:40 +00:00
|
|
|
{
|
2008-11-28 04:58:32 +00:00
|
|
|
switch (size) {
|
|
|
|
case 4: {
|
|
|
|
int32_t v = src;
|
|
|
|
memcpy(dst, &v, 4);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case 8: {
|
|
|
|
int64_t v = src;
|
|
|
|
memcpy(dst, &v, 8);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default: abort(s);
|
|
|
|
}
|
2008-11-27 20:59:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class ImmediateListener: public Promise::Listener {
|
|
|
|
public:
|
2008-12-02 02:38:00 +00:00
|
|
|
ImmediateListener(System* s, void* dst, unsigned size, unsigned offset):
|
|
|
|
s(s), dst(dst), size(size), offset(offset)
|
2008-11-28 04:58:32 +00:00
|
|
|
{ }
|
2008-11-27 20:59:40 +00:00
|
|
|
|
2009-03-11 01:08:16 +00:00
|
|
|
virtual bool resolve(int64_t value, void** location) {
|
2008-11-28 04:58:32 +00:00
|
|
|
copy(s, dst, value, size);
|
2009-03-11 01:08:16 +00:00
|
|
|
if (location) *location = static_cast<uint8_t*>(dst) + offset;
|
|
|
|
return offset == 0;
|
2008-11-27 20:59:40 +00:00
|
|
|
}
|
|
|
|
|
2008-11-28 04:58:32 +00:00
|
|
|
System* s;
|
2008-11-27 20:59:40 +00:00
|
|
|
void* dst;
|
2008-11-28 04:58:32 +00:00
|
|
|
unsigned size;
|
2008-12-02 02:38:00 +00:00
|
|
|
unsigned offset;
|
2008-11-27 20:59:40 +00:00
|
|
|
};
|
|
|
|
|
2008-02-17 20:57:40 +00:00
|
|
|
class ImmediateTask: public Task {
|
|
|
|
public:
|
2009-02-09 23:22:01 +00:00
|
|
|
ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size,
|
2008-12-02 02:38:00 +00:00
|
|
|
unsigned promiseOffset):
|
2008-02-17 20:57:40 +00:00
|
|
|
Task(next),
|
|
|
|
promise(promise),
|
2008-11-14 00:59:21 +00:00
|
|
|
offset(offset),
|
2008-12-02 02:38:00 +00:00
|
|
|
size(size),
|
|
|
|
promiseOffset(promiseOffset)
|
2008-02-17 20:57:40 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void run(Context* c) {
|
2008-11-23 23:58:01 +00:00
|
|
|
if (promise->resolved()) {
|
2009-02-09 23:22:01 +00:00
|
|
|
copy(c->s, c->result + offset->value(), promise->value(), size);
|
2008-11-27 20:59:40 +00:00
|
|
|
} else {
|
2009-02-09 23:22:01 +00:00
|
|
|
new (promise->listen(sizeof(ImmediateListener))) ImmediateListener
|
|
|
|
(c->s, c->result + offset->value(), size, promiseOffset);
|
2008-11-14 00:59:21 +00:00
|
|
|
}
|
2008-02-17 20:57:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Promise* promise;
|
2008-09-09 00:31:19 +00:00
|
|
|
Promise* offset;
|
2008-11-14 00:59:21 +00:00
|
|
|
unsigned size;
|
2008-12-02 02:38:00 +00:00
|
|
|
unsigned promiseOffset;
|
2008-02-17 20:57:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void
|
2008-11-14 00:59:21 +00:00
|
|
|
appendImmediateTask(Context* c, Promise* promise, Promise* offset,
|
2008-12-02 02:38:00 +00:00
|
|
|
unsigned size, unsigned promiseOffset = 0)
|
2008-02-17 20:57:40 +00:00
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
c->tasks = new(c->zone) ImmediateTask
|
2009-02-09 23:22:51 +00:00
|
|
|
(c->tasks, promise, offset, size, promiseOffset);
|
2008-02-17 20:57:40 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 02:21:52 +00:00
|
|
|
class AlignmentPadding {
|
|
|
|
public:
|
2009-10-18 00:18:03 +00:00
|
|
|
AlignmentPadding(Context* c, unsigned instructionOffset, unsigned alignment):
|
|
|
|
offset(c->code.length()),
|
|
|
|
instructionOffset(instructionOffset),
|
|
|
|
alignment(alignment),
|
2011-07-17 01:12:45 +00:00
|
|
|
next(0),
|
|
|
|
padding(-1)
|
2009-10-18 00:18:03 +00:00
|
|
|
{
|
2008-09-08 02:21:52 +00:00
|
|
|
if (c->lastBlock->firstPadding) {
|
|
|
|
c->lastBlock->lastPadding->next = this;
|
|
|
|
} else {
|
|
|
|
c->lastBlock->firstPadding = this;
|
|
|
|
}
|
|
|
|
c->lastBlock->lastPadding = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned offset;
|
2009-10-18 00:18:03 +00:00
|
|
|
unsigned instructionOffset;
|
|
|
|
unsigned alignment;
|
2008-09-08 02:21:52 +00:00
|
|
|
AlignmentPadding* next;
|
2011-07-17 01:12:45 +00:00
|
|
|
int padding;
|
2008-09-08 02:21:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
unsigned
|
2009-02-15 00:52:44 +00:00
|
|
|
padding(AlignmentPadding* p, unsigned start, unsigned offset,
|
|
|
|
AlignmentPadding* limit)
|
2008-09-08 02:21:52 +00:00
|
|
|
{
|
|
|
|
unsigned padding = 0;
|
2009-02-15 00:52:44 +00:00
|
|
|
if (limit) {
|
2011-07-17 01:12:45 +00:00
|
|
|
if (limit->padding == -1) {
|
|
|
|
for (; p; p = p->next) {
|
|
|
|
if (p->padding == -1) {
|
|
|
|
unsigned index = p->offset - offset;
|
|
|
|
while ((start + index + padding + p->instructionOffset)
|
|
|
|
% p->alignment)
|
|
|
|
{
|
|
|
|
++ padding;
|
|
|
|
}
|
2009-02-15 00:52:44 +00:00
|
|
|
|
2011-07-17 01:12:45 +00:00
|
|
|
p->padding = padding;
|
|
|
|
|
|
|
|
if (p == limit) break;
|
|
|
|
} else {
|
|
|
|
padding = p->padding;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
padding = limit->padding;
|
2008-09-08 02:21:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return padding;
|
|
|
|
}
|
|
|
|
|
2009-10-10 23:46:43 +00:00
|
|
|
extern "C" bool
|
|
|
|
detectFeature(unsigned ecx, unsigned edx);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bool
|
2009-10-10 23:46:43 +00:00
|
|
|
useSSE(ArchitectureContext* c)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-10-18 00:18:03 +00:00
|
|
|
// amd64 implies SSE2 support
|
|
|
|
return true;
|
|
|
|
} else if (c->useNativeFeatures) {
|
2009-10-10 23:46:43 +00:00
|
|
|
static int supported = -1;
|
|
|
|
if (supported == -1) {
|
|
|
|
supported = detectFeature(0, 0x2000000) // SSE 1
|
|
|
|
and detectFeature(0, 0x4000000); // SSE 2
|
2009-10-10 21:03:23 +00:00
|
|
|
}
|
2009-10-10 23:46:43 +00:00
|
|
|
return supported;
|
|
|
|
} else {
|
|
|
|
return false;
|
2009-10-10 21:03:23 +00:00
|
|
|
}
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
#define REX_W 0x48
|
|
|
|
#define REX_R 0x44
|
|
|
|
#define REX_X 0x42
|
|
|
|
#define REX_B 0x41
|
|
|
|
#define REX_NONE 0x40
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-09-20 21:43:32 +00:00
|
|
|
void maybeRex(Context* c, unsigned size, int a, int index, int base,
|
|
|
|
bool always)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-06-11 15:40:50 +00:00
|
|
|
uint8_t byte;
|
2009-10-10 23:46:43 +00:00
|
|
|
if (size == 8) {
|
2009-06-11 15:40:50 +00:00
|
|
|
byte = REX_W;
|
|
|
|
} else {
|
|
|
|
byte = REX_NONE;
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
2009-10-10 23:46:43 +00:00
|
|
|
if (a != NoRegister and (a & 8)) byte |= REX_R;
|
|
|
|
if (index != NoRegister and (index & 8)) byte |= REX_X;
|
|
|
|
if (base != NoRegister and (base & 8)) byte |= REX_B;
|
|
|
|
if (always or byte != REX_NONE) c->code.append(byte);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
2009-06-11 15:40:50 +00:00
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
maybeRex(Context* c, unsigned size, Assembler::Register* a,
|
|
|
|
Assembler::Register* b)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, size, a->low, NoRegister, b->low, false);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
alwaysRex(Context* c, unsigned size, Assembler::Register* a,
|
|
|
|
Assembler::Register* b)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, size, a->low, NoRegister, b->low, true);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
maybeRex(Context* c, unsigned size, Assembler::Register* a)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, size, NoRegister, NoRegister, a->low, false);
|
2008-04-13 19:48:20 +00:00
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
maybeRex(Context* c, unsigned size, Assembler::Register* a,
|
|
|
|
Assembler::Memory* b)
|
|
|
|
{
|
2010-02-02 18:37:08 +00:00
|
|
|
maybeRex(c, size, a->low, b->index, b->base, size == 1 and (a->low & 4));
|
2009-06-11 15:40:50 +00:00
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
maybeRex(Context* c, unsigned size, Assembler::Memory* a)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, size, NoRegister, a->index, a->base, false);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
int
|
|
|
|
regCode(int a)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
return a & 7;
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
int
|
|
|
|
regCode(Assembler::Register* a)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
return regCode(a->low);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
modrm(Context* c, uint8_t mod, int a, int b)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
c->code.append(mod | (regCode(b) << 3) | regCode(a));
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
modrm(Context* c, uint8_t mod, Assembler::Register* a, Assembler::Register* b)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
modrm(c, mod, a->low, b->low);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
sib(Context* c, unsigned scale, int index, int base)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
c->code.append((log(scale) << 6) | (regCode(index) << 3) | regCode(base));
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
modrmSib(Context* c, int width, int a, int scale, int index, int base)
|
2009-09-20 21:43:32 +00:00
|
|
|
{
|
2009-10-10 23:46:43 +00:00
|
|
|
if (index == NoRegister) {
|
2009-06-11 15:40:50 +00:00
|
|
|
modrm(c, width, base, a);
|
2009-10-10 23:46:43 +00:00
|
|
|
if (regCode(base) == rsp) {
|
2009-06-11 15:40:50 +00:00
|
|
|
sib(c, 0x00, rsp, rsp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
modrm(c, width, rsp, a);
|
|
|
|
sib(c, scale, index, base);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
2009-06-11 15:40:50 +00:00
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
modrmSibImm(Context* c, int a, int scale, int index, int base, int offset)
|
2009-09-20 21:43:32 +00:00
|
|
|
{
|
2009-10-10 23:46:43 +00:00
|
|
|
if (offset == 0 and regCode(base) != rbp) {
|
2009-06-11 15:40:50 +00:00
|
|
|
modrmSib(c, 0x00, a, scale, index, base);
|
2009-10-10 23:46:43 +00:00
|
|
|
} else if (isInt8(offset)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
modrmSib(c, 0x40, a, scale, index, base);
|
|
|
|
c->code.append(offset);
|
|
|
|
} else {
|
|
|
|
modrmSib(c, 0x80, a, scale, index, base);
|
|
|
|
c->code.append4(offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
modrmSibImm(Context* c, Assembler::Register* a, Assembler::Memory* b)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
modrmSibImm(c, a->low, b->scale, b->index, b->base, b->offset);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
opcode(Context* c, uint8_t op)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
c->code.append(op);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
opcode(Context* c, uint8_t op1, uint8_t op2)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
c->code.append(op1);
|
|
|
|
c->code.append(op2);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
opcode(Context* c, uint8_t op1, uint8_t op2, uint8_t op3)
|
|
|
|
{
|
2009-08-06 16:26:22 +00:00
|
|
|
c->code.append(op1);
|
|
|
|
c->code.append(op2);
|
|
|
|
c->code.append(op3);
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
void
|
|
|
|
return_(Context* c)
|
|
|
|
{
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xc3);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
void
|
|
|
|
trap(Context* c)
|
|
|
|
{
|
|
|
|
opcode(c, 0xcc);
|
|
|
|
}
|
|
|
|
|
2009-03-03 03:18:15 +00:00
|
|
|
void
|
|
|
|
ignore(Context*)
|
|
|
|
{ }
|
|
|
|
|
2009-11-30 15:38:16 +00:00
|
|
|
void
|
|
|
|
storeLoadBarrier(Context* c)
|
|
|
|
{
|
|
|
|
if (useSSE(c->ac)) {
|
|
|
|
// mfence:
|
|
|
|
c->code.append(0x0f);
|
|
|
|
c->code.append(0xae);
|
|
|
|
c->code.append(0xf0);
|
|
|
|
} else {
|
|
|
|
// lock addq $0x0,(%rsp):
|
|
|
|
c->code.append(0xf0);
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-11-30 15:38:16 +00:00
|
|
|
c->code.append(0x48);
|
|
|
|
}
|
|
|
|
c->code.append(0x83);
|
|
|
|
c->code.append(0x04);
|
|
|
|
c->code.append(0x24);
|
|
|
|
c->code.append(0x00);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
void
|
|
|
|
unconditional(Context* c, unsigned jump, Assembler::Constant* a)
|
|
|
|
{
|
2008-09-09 00:31:19 +00:00
|
|
|
appendOffsetTask(c, a->value, offset(c), 5);
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, jump);
|
2008-02-12 00:20:32 +00:00
|
|
|
c->code.append4(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
conditional(Context* c, unsigned condition, Assembler::Constant* a)
|
|
|
|
{
|
2008-09-09 00:31:19 +00:00
|
|
|
appendOffsetTask(c, a->value, offset(c), 6);
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x0f, condition);
|
2008-02-12 00:20:32 +00:00
|
|
|
c->code.append4(0);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned
|
|
|
|
index(ArchitectureContext*, UnaryOperation operation, OperandType operand)
|
2008-06-02 13:49:09 +00:00
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
return operation + (UnaryOperationCount * operand);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned
|
|
|
|
index(ArchitectureContext*, BinaryOperation operation,
|
2008-08-19 23:38:37 +00:00
|
|
|
OperandType operand1,
|
|
|
|
OperandType operand2)
|
2008-02-17 22:29:04 +00:00
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
return operation
|
2009-10-10 21:03:23 +00:00
|
|
|
+ ((BinaryOperationCount + NonBranchTernaryOperationCount) * operand1)
|
|
|
|
+ ((BinaryOperationCount + NonBranchTernaryOperationCount)
|
2008-09-06 21:25:41 +00:00
|
|
|
* OperandTypeCount * operand2);
|
2008-02-17 22:29:04 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bool
|
|
|
|
isBranch(TernaryOperation op)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
return op > FloatMin;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
isFloatBranch(TernaryOperation op)
|
|
|
|
{
|
|
|
|
return op > JumpIfNotEqual;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned
|
|
|
|
index(ArchitectureContext* c UNUSED, TernaryOperation operation,
|
|
|
|
OperandType operand1, OperandType operand2)
|
|
|
|
{
|
|
|
|
assert(c, not isBranch(operation));
|
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
return BinaryOperationCount + operation
|
2009-10-10 21:03:23 +00:00
|
|
|
+ ((BinaryOperationCount + NonBranchTernaryOperationCount) * operand1)
|
|
|
|
+ ((BinaryOperationCount + NonBranchTernaryOperationCount)
|
2008-09-06 21:25:41 +00:00
|
|
|
* OperandTypeCount * operand2);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned
|
|
|
|
branchIndex(ArchitectureContext* c UNUSED, OperandType operand1,
|
|
|
|
OperandType operand2)
|
|
|
|
{
|
|
|
|
return operand1 + (OperandTypeCount * operand2);
|
|
|
|
}
|
|
|
|
|
2008-04-17 22:07:32 +00:00
|
|
|
void
|
2009-02-09 23:22:01 +00:00
|
|
|
moveCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b);
|
2008-04-17 22:07:32 +00:00
|
|
|
|
2008-12-02 02:38:00 +00:00
|
|
|
void
|
2009-02-09 23:22:01 +00:00
|
|
|
moveCR2(Context*, unsigned, Assembler::Constant*, unsigned,
|
|
|
|
Assembler::Register*, unsigned);
|
2008-12-02 02:38:00 +00:00
|
|
|
|
2008-04-17 22:07:32 +00:00
|
|
|
void
|
|
|
|
callR(Context*, unsigned, Assembler::Register*);
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
void
|
2008-06-02 14:13:20 +00:00
|
|
|
callC(Context* c, unsigned size UNUSED, Assembler::Constant* a)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2008-06-02 13:49:09 +00:00
|
|
|
unconditional(c, 0xe8, a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
longCallC(Context* c, unsigned size, Assembler::Constant* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2008-06-02 13:49:09 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-10-18 00:18:03 +00:00
|
|
|
Assembler::Register r(LongJumpRegister);
|
2009-02-09 23:22:01 +00:00
|
|
|
moveCR2(c, size, a, size, &r, 11);
|
2008-04-13 19:48:20 +00:00
|
|
|
callR(c, size, &r);
|
|
|
|
} else {
|
2008-06-02 13:49:09 +00:00
|
|
|
callC(c, size, a);
|
2008-04-13 19:48:20 +00:00
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
void
|
|
|
|
jumpR(Context* c, unsigned size UNUSED, Assembler::Register* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, 4, a);
|
|
|
|
opcode(c, 0xff, 0xe0 + regCode(a));
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
jumpC(Context* c, unsigned size UNUSED, Assembler::Constant* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
|
|
|
unconditional(c, 0xe9, a);
|
|
|
|
}
|
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
void
|
|
|
|
jumpM(Context* c, unsigned size UNUSED, Assembler::Memory* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2009-06-11 15:40:50 +00:00
|
|
|
|
|
|
|
maybeRex(c, 4, a);
|
|
|
|
opcode(c, 0xff);
|
|
|
|
modrmSibImm(c, rsp, a->scale, a->index, a->base, a->offset);
|
2008-09-09 00:31:19 +00:00
|
|
|
}
|
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
void
|
|
|
|
longJumpC(Context* c, unsigned size, Assembler::Constant* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-10-18 00:18:03 +00:00
|
|
|
Assembler::Register r(LongJumpRegister);
|
2009-02-09 23:22:51 +00:00
|
|
|
moveCR2(c, size, a, size, &r, 11);
|
2008-09-06 21:25:41 +00:00
|
|
|
jumpR(c, size, &r);
|
|
|
|
} else {
|
2009-02-09 23:22:51 +00:00
|
|
|
jumpC(c, size, a);
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
callR(Context* c, unsigned size UNUSED, Assembler::Register* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
// maybeRex.W has no meaning here so we disable it
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, 4, a);
|
|
|
|
opcode(c, 0xff, 0xd0 + regCode(a));
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
|
2008-10-19 00:15:57 +00:00
|
|
|
void
|
|
|
|
callM(Context* c, unsigned size UNUSED, Assembler::Memory* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2009-06-11 15:40:50 +00:00
|
|
|
|
|
|
|
maybeRex(c, 4, a);
|
|
|
|
opcode(c, 0xff);
|
|
|
|
modrmSibImm(c, rdx, a->scale, a->index, a->base, a->offset);
|
2008-10-19 00:15:57 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 02:21:52 +00:00
|
|
|
void
|
|
|
|
alignedCallC(Context* c, unsigned size, Assembler::Constant* a)
|
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
new(c->zone) AlignmentPadding(c, 1, 4);
|
2008-09-08 02:21:52 +00:00
|
|
|
callC(c, size, a);
|
|
|
|
}
|
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
void
|
|
|
|
alignedLongCallC(Context* c, unsigned size, Assembler::Constant* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2009-10-18 00:18:03 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2012-05-08 22:13:17 +00:00
|
|
|
new (c->zone) AlignmentPadding(c, 2, 8);
|
2009-10-18 00:18:03 +00:00
|
|
|
longCallC(c, size, a);
|
|
|
|
} else {
|
|
|
|
alignedCallC(c, size, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-22 01:39:25 +00:00
|
|
|
void
|
|
|
|
alignedJumpC(Context* c, unsigned size, Assembler::Constant* a)
|
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
new (c->zone) AlignmentPadding(c, 1, 4);
|
2009-04-22 01:39:25 +00:00
|
|
|
jumpC(c, size, a);
|
|
|
|
}
|
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
void
|
|
|
|
alignedLongJumpC(Context* c, unsigned size, Assembler::Constant* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size == TargetBytesPerWord);
|
2009-10-18 00:18:03 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2012-05-08 22:13:17 +00:00
|
|
|
new (c->zone) AlignmentPadding(c, 2, 8);
|
2009-10-18 00:18:03 +00:00
|
|
|
longJumpC(c, size, a);
|
|
|
|
} else {
|
|
|
|
alignedJumpC(c, size, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-05 15:00:38 +00:00
|
|
|
void
|
|
|
|
pushR(Context* c, unsigned size, Assembler::Register* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and size == 8) {
|
2008-09-05 15:00:38 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
|
|
|
|
pushR(c, 4, &ah);
|
|
|
|
pushR(c, 4, a);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, 4, a);
|
|
|
|
opcode(c, 0x50 + regCode(a));
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
void
|
|
|
|
moveRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize, Assembler::Register* b);
|
|
|
|
|
|
|
|
void
|
|
|
|
popR(Context* c, unsigned size, Assembler::Register* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and size == 8) {
|
2008-09-06 21:25:41 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
|
|
|
|
popR(c, 4, a);
|
|
|
|
popR(c, 4, &ah);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, 4, a);
|
|
|
|
opcode(c, 0x58 + regCode(a));
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8 and size == 4) {
|
2008-09-06 21:25:41 +00:00
|
|
|
moveRR(c, 4, a, 8, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-15 00:45:31 +00:00
|
|
|
void
|
|
|
|
addCarryCR(Context* c, unsigned size, Assembler::Constant* a,
|
|
|
|
Assembler::Register* b);
|
|
|
|
|
|
|
|
void
|
|
|
|
negateR(Context* c, unsigned size, Assembler::Register* a)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and size == 8) {
|
2008-10-15 00:45:31 +00:00
|
|
|
assert(c, a->low == rax and a->high == rdx);
|
|
|
|
|
|
|
|
ResolvedPromise zeroPromise(0);
|
|
|
|
Assembler::Constant zero(&zeroPromise);
|
|
|
|
|
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
|
|
|
|
negateR(c, 4, a);
|
|
|
|
addCarryCR(c, 4, &zero, &ah);
|
|
|
|
negateR(c, 4, &ah);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, size, a);
|
|
|
|
opcode(c, 0xf7, 0xd8 + regCode(a));
|
2008-10-15 00:45:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
negateRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2008-11-12 01:09:45 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b UNUSED)
|
2008-10-15 00:45:31 +00:00
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
|
|
|
negateR(c, aSize, a);
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
void
|
2009-06-11 15:40:50 +00:00
|
|
|
moveCR2(Context* c, UNUSED unsigned aSize, Assembler::Constant* a,
|
|
|
|
UNUSED unsigned bSize, Assembler::Register* b, unsigned promiseOffset)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-04-28 22:08:31 +00:00
|
|
|
int64_t v = a->value->value();
|
|
|
|
|
|
|
|
ResolvedPromise high((v >> 32) & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant ah(&high);
|
|
|
|
|
|
|
|
ResolvedPromise low(v & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant al(&low);
|
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2009-02-09 23:22:01 +00:00
|
|
|
moveCR(c, 4, &al, 4, b);
|
|
|
|
moveCR(c, 4, &ah, 4, &bh);
|
2008-02-17 20:57:40 +00:00
|
|
|
} else {
|
2011-08-30 01:00:17 +00:00
|
|
|
maybeRex(c, TargetBytesPerWord, b);
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xb8 + regCode(b));
|
2008-04-28 22:08:31 +00:00
|
|
|
if (a->value->resolved()) {
|
2011-09-01 03:18:00 +00:00
|
|
|
c->code.appendTargetAddress(a->value->value());
|
2008-04-28 22:08:31 +00:00
|
|
|
} else {
|
2008-12-02 02:38:00 +00:00
|
|
|
appendImmediateTask
|
2011-08-30 01:00:17 +00:00
|
|
|
(c, a->value, offset(c), TargetBytesPerWord, promiseOffset);
|
2011-09-01 03:18:00 +00:00
|
|
|
c->code.appendTargetAddress(static_cast<target_uintptr_t>(0));
|
2008-04-28 22:08:31 +00:00
|
|
|
}
|
2008-02-17 20:57:40 +00:00
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bool
|
|
|
|
floatReg(Assembler::Register* a)
|
|
|
|
{
|
|
|
|
return a->low >= xmm0;
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sseMoveRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-11-30 02:17:08 +00:00
|
|
|
assert(c, aSize >= 4);
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
if (floatReg(a) and floatReg(b)) {
|
|
|
|
if (aSize == 4) {
|
|
|
|
opcode(c, 0xf3);
|
|
|
|
maybeRex(c, 4, a, b);
|
|
|
|
opcode(c, 0x0f, 0x10);
|
2009-12-01 02:06:01 +00:00
|
|
|
modrm(c, 0xc0, a, b);
|
2009-10-10 21:03:23 +00:00
|
|
|
} else {
|
|
|
|
opcode(c, 0xf2);
|
2011-07-06 17:09:06 +00:00
|
|
|
maybeRex(c, 4, b, a);
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x0f, 0x10);
|
2009-12-01 02:06:01 +00:00
|
|
|
modrm(c, 0xc0, a, b);
|
2009-10-10 21:03:23 +00:00
|
|
|
}
|
|
|
|
} else if (floatReg(a)) {
|
|
|
|
opcode(c, 0x66);
|
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x0f, 0x7e);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2009-08-06 16:26:22 +00:00
|
|
|
} else {
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x66);
|
|
|
|
maybeRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0x6e);
|
|
|
|
modrm(c, 0xc0, a, b);
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
void
|
2009-10-04 22:10:36 +00:00
|
|
|
sseMoveCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, aSize <= TargetBytesPerWord);
|
2009-10-04 22:10:36 +00:00
|
|
|
Assembler::Register tmp(c->client->acquireTemporary(GeneralRegisterMask));
|
|
|
|
moveCR2(c, aSize, a, aSize, &tmp, 0);
|
|
|
|
sseMoveRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
2009-10-04 19:56:48 +00:00
|
|
|
|
2009-10-04 22:10:36 +00:00
|
|
|
void
|
|
|
|
moveCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
if (floatReg(b)) {
|
|
|
|
sseMoveCR(c, aSize, a, bSize, b);
|
|
|
|
} else {
|
|
|
|
moveCR2(c, aSize, a, bSize, b, 0);
|
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-11-11 02:11:32 +00:00
|
|
|
void
|
|
|
|
swapRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, aSize == TargetBytesPerWord);
|
2008-11-11 02:11:32 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
alwaysRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x87);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-11-11 02:11:32 +00:00
|
|
|
}
|
|
|
|
|
2008-09-05 15:00:38 +00:00
|
|
|
void
|
|
|
|
moveRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-06-11 15:40:50 +00:00
|
|
|
UNUSED unsigned bSize, Assembler::Register* b)
|
2008-09-05 15:00:38 +00:00
|
|
|
{
|
2009-10-10 23:46:43 +00:00
|
|
|
if (floatReg(a) or floatReg(b)) {
|
2009-10-10 21:03:23 +00:00
|
|
|
sseMoveRR(c, aSize, a, bSize, b);
|
|
|
|
return;
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8 and bSize == 8) {
|
2008-09-05 15:00:38 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
2008-11-11 02:11:32 +00:00
|
|
|
if (a->high == b->low) {
|
|
|
|
if (a->low == b->high) {
|
|
|
|
swapRR(c, 4, a, 4, b);
|
|
|
|
} else {
|
|
|
|
moveRR(c, 4, &ah, 4, &bh);
|
|
|
|
moveRR(c, 4, a, 4, b);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
moveRR(c, 4, a, 4, b);
|
|
|
|
moveRR(c, 4, &ah, 4, &bh);
|
|
|
|
}
|
2008-09-05 15:00:38 +00:00
|
|
|
} else {
|
|
|
|
switch (aSize) {
|
|
|
|
case 1:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and a->low > rbx) {
|
2008-09-05 15:00:38 +00:00
|
|
|
assert(c, b->low <= rbx);
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
moveRR(c, TargetBytesPerWord, a, TargetBytesPerWord, b);
|
|
|
|
moveRR(c, 1, b, TargetBytesPerWord, b);
|
2008-09-05 15:00:38 +00:00
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
alwaysRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0xbe);
|
|
|
|
modrm(c, 0xc0, a, b);
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
2009-06-11 15:40:50 +00:00
|
|
|
alwaysRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0xbf);
|
|
|
|
modrm(c, 0xc0, a, b);
|
2008-09-05 15:00:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
2009-06-11 15:40:50 +00:00
|
|
|
if (bSize == 8) {
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-08-07 20:48:30 +00:00
|
|
|
alwaysRex(c, bSize, b, a);
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x63);
|
|
|
|
modrm(c, 0xc0, a, b);
|
|
|
|
} else {
|
|
|
|
if (a->low == rax and b->low == rax and b->high == rdx) {
|
|
|
|
opcode(c, 0x99); //cdq
|
|
|
|
} else {
|
2008-09-05 15:00:38 +00:00
|
|
|
assert(c, b->low == rax and b->high == rdx);
|
|
|
|
|
|
|
|
moveRR(c, 4, a, 4, b);
|
|
|
|
moveRR(c, 4, b, 8, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (a->low != b->low) {
|
2009-06-11 15:40:50 +00:00
|
|
|
alwaysRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x89);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
}
|
2009-06-11 15:40:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
if (a->low != b->low){
|
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x89);
|
|
|
|
modrm(c, 0xc0, b, a);
|
|
|
|
}
|
2008-09-05 15:00:38 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-06-11 15:40:50 +00:00
|
|
|
}
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
void
|
|
|
|
sseMoveMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-04 19:56:48 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-11-30 02:17:08 +00:00
|
|
|
assert(c, aSize >= 4);
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2009-10-04 19:56:48 +00:00
|
|
|
opcode(c, 0xf3);
|
|
|
|
opcode(c, 0x0f, 0x7e);
|
|
|
|
modrmSibImm(c, b, a);
|
|
|
|
} else {
|
|
|
|
opcode(c, 0x66);
|
|
|
|
maybeRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0x6e);
|
|
|
|
modrmSibImm(c, b, a);
|
|
|
|
}
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
2008-09-05 15:00:38 +00:00
|
|
|
void
|
2008-09-06 21:25:41 +00:00
|
|
|
moveMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2008-09-08 02:21:52 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
2008-09-05 15:00:38 +00:00
|
|
|
{
|
2009-10-10 23:46:43 +00:00
|
|
|
if (floatReg(b)) {
|
|
|
|
sseMoveMR(c, aSize, a, bSize, b);
|
|
|
|
return;
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
switch (aSize) {
|
|
|
|
case 1:
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0xbe);
|
|
|
|
modrmSibImm(c, b, a);
|
2008-09-06 21:25:41 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0xbf);
|
|
|
|
modrmSibImm(c, b, a);
|
2008-09-06 21:25:41 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b, a);
|
|
|
|
opcode(c, 0x63);
|
|
|
|
modrmSibImm(c, b, a);
|
|
|
|
} else {
|
|
|
|
if (bSize == 8) {
|
2008-09-08 02:21:52 +00:00
|
|
|
assert(c, b->low == rax and b->high == rdx);
|
|
|
|
|
|
|
|
moveMR(c, 4, a, 4, b);
|
|
|
|
moveRR(c, 4, b, 8, b);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b, a);
|
|
|
|
opcode(c, 0x8b);
|
|
|
|
modrmSibImm(c, b, a);
|
2008-09-08 02:21:52 +00:00
|
|
|
}
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-06-11 15:40:50 +00:00
|
|
|
|
|
|
|
case 8:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2009-06-11 15:40:50 +00:00
|
|
|
Assembler::Memory ah(a->base, a->offset + 4, a->index, a->scale);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
moveMR(c, 4, a, 4, b);
|
|
|
|
moveMR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
|
|
|
maybeRex(c, bSize, b, a);
|
|
|
|
opcode(c, 0x8b);
|
|
|
|
modrmSibImm(c, b, a);
|
|
|
|
}
|
|
|
|
break;
|
2008-09-06 21:25:41 +00:00
|
|
|
|
|
|
|
default: abort(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
void
|
|
|
|
sseMoveRM(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
UNUSED unsigned bSize, Assembler::Memory* b)
|
|
|
|
{
|
2009-11-30 02:17:08 +00:00
|
|
|
assert(c, aSize >= 4);
|
2009-10-04 19:56:48 +00:00
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2009-10-04 19:56:48 +00:00
|
|
|
opcode(c, 0x66);
|
|
|
|
opcode(c, 0x0f, 0xd6);
|
|
|
|
modrmSibImm(c, a, b);
|
|
|
|
} else {
|
|
|
|
opcode(c, 0x66);
|
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x0f, 0x7e);
|
|
|
|
modrmSibImm(c, a, b);
|
|
|
|
}
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
void
|
|
|
|
moveRM(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Memory* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
2009-06-11 15:40:50 +00:00
|
|
|
|
2009-10-10 23:46:43 +00:00
|
|
|
if (floatReg(a)) {
|
|
|
|
sseMoveRM(c, aSize, a, bSize, b);
|
|
|
|
return;
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
switch (aSize) {
|
|
|
|
case 1:
|
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0x88);
|
|
|
|
modrmSibImm(c, a, b);
|
|
|
|
break;
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
case 2:
|
|
|
|
opcode(c, 0x66);
|
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0x89);
|
|
|
|
modrmSibImm(c, a, b);
|
|
|
|
break;
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
case 4:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0x89);
|
|
|
|
modrmSibImm(c, a, b);
|
2008-09-06 21:25:41 +00:00
|
|
|
break;
|
2009-06-11 15:40:50 +00:00
|
|
|
} else {
|
|
|
|
opcode(c, 0x89);
|
|
|
|
modrmSibImm(c, a, b);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0x89);
|
|
|
|
modrmSibImm(c, a, b);
|
|
|
|
} else {
|
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Memory bh(b->base, b->offset + 4, b->index, b->scale);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
moveRM(c, 4, a, 4, b);
|
|
|
|
moveRM(c, 4, &ah, 4, &bh);
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
2009-06-11 15:40:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: abort(c);
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-08 02:21:52 +00:00
|
|
|
void
|
|
|
|
moveAR(Context* c, unsigned aSize, Assembler::Address* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, TargetBytesPerWord == 8 or (aSize == 4 and bSize == 4));
|
2008-09-08 02:21:52 +00:00
|
|
|
|
|
|
|
Assembler::Constant constant(a->address);
|
|
|
|
Assembler::Memory memory(b->low, 0, -1, 0);
|
|
|
|
|
|
|
|
moveCR(c, aSize, &constant, bSize, b);
|
|
|
|
moveMR(c, bSize, &memory, bSize, b);
|
|
|
|
}
|
|
|
|
|
2008-11-14 00:59:21 +00:00
|
|
|
ShiftMaskPromise*
|
|
|
|
shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask)
|
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
return new(c->zone) ShiftMaskPromise(base, shift, mask);
|
2008-11-14 00:59:21 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 02:21:52 +00:00
|
|
|
void
|
|
|
|
moveCM(Context* c, unsigned aSize UNUSED, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Memory* b)
|
|
|
|
{
|
|
|
|
switch (bSize) {
|
|
|
|
case 1:
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b);
|
|
|
|
opcode(c, 0xc6);
|
|
|
|
modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset);
|
2008-09-08 02:21:52 +00:00
|
|
|
c->code.append(a->value->value());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x66);
|
|
|
|
maybeRex(c, bSize, b);
|
|
|
|
opcode(c, 0xc7);
|
|
|
|
modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset);
|
2008-09-08 02:21:52 +00:00
|
|
|
c->code.append2(a->value->value());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b);
|
|
|
|
opcode(c, 0xc7);
|
|
|
|
modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset);
|
2008-11-11 18:56:43 +00:00
|
|
|
if (a->value->resolved()) {
|
|
|
|
c->code.append4(a->value->value());
|
|
|
|
} else {
|
2009-02-09 23:22:51 +00:00
|
|
|
appendImmediateTask(c, a->value, offset(c), 4);
|
2008-11-11 18:56:43 +00:00
|
|
|
c->code.append4(0);
|
|
|
|
}
|
2008-09-08 02:21:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: {
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) {
|
2009-10-10 23:46:43 +00:00
|
|
|
if (a->value->resolved() and isInt32(a->value->value())) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b);
|
|
|
|
opcode(c, 0xc7);
|
|
|
|
modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset);
|
2009-02-10 02:00:53 +00:00
|
|
|
c->code.append4(a->value->value());
|
|
|
|
} else {
|
2009-09-20 21:43:32 +00:00
|
|
|
Assembler::Register tmp
|
|
|
|
(c->client->acquireTemporary(GeneralRegisterMask));
|
2009-02-10 02:00:53 +00:00
|
|
|
moveCR(c, 8, a, 8, &tmp);
|
|
|
|
moveRM(c, 8, &tmp, 8, b);
|
2009-03-01 23:07:28 +00:00
|
|
|
c->client->releaseTemporary(tmp.low);
|
2009-02-10 02:00:53 +00:00
|
|
|
}
|
2009-09-20 21:43:32 +00:00
|
|
|
} else {
|
|
|
|
Assembler::Constant ah(shiftMaskPromise(c, a->value, 32, 0xFFFFFFFF));
|
2008-11-26 02:22:21 +00:00
|
|
|
Assembler::Constant al(shiftMaskPromise(c, a->value, 0, 0xFFFFFFFF));
|
2008-09-08 02:21:52 +00:00
|
|
|
|
2008-11-26 02:22:21 +00:00
|
|
|
Assembler::Memory bh(b->base, b->offset + 4, b->index, b->scale);
|
2008-09-08 02:21:52 +00:00
|
|
|
|
2008-11-26 02:22:21 +00:00
|
|
|
moveCM(c, 4, &al, 4, b);
|
|
|
|
moveCM(c, 4, &ah, 4, &bh);
|
|
|
|
}
|
2008-09-08 02:21:52 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default: abort(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-27 22:00:58 +00:00
|
|
|
void
|
|
|
|
moveZRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
switch (aSize) {
|
|
|
|
case 2:
|
2009-06-11 15:40:50 +00:00
|
|
|
alwaysRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0xb7);
|
|
|
|
modrm(c, 0xc0, a, b);
|
2008-09-27 22:00:58 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: abort(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-19 00:15:57 +00:00
|
|
|
void
|
|
|
|
moveZMR(Context* c, unsigned aSize UNUSED, Assembler::Memory* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, bSize == TargetBytesPerWord);
|
2008-10-19 00:15:57 +00:00
|
|
|
assert(c, aSize == 2);
|
2009-06-11 15:40:50 +00:00
|
|
|
|
|
|
|
maybeRex(c, bSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0xb7);
|
|
|
|
modrmSibImm(c, b->low, a->scale, a->index, a->base, a->offset);
|
2008-10-19 00:15:57 +00:00
|
|
|
}
|
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
void
|
2008-09-07 20:12:11 +00:00
|
|
|
addCarryRR(Context* c, unsigned size, Assembler::Register* a,
|
|
|
|
Assembler::Register* b)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, TargetBytesPerWord == 8 or size == 4);
|
2008-09-07 20:12:11 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, size, a, b);
|
|
|
|
opcode(c, 0x11);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-09-07 20:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
addRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2008-09-07 20:12:11 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
addRR(c, 4, a, 4, b);
|
|
|
|
addCarryRR(c, 4, &ah, &bh);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x01);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-09-07 20:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-30 23:58:51 +00:00
|
|
|
addCarryCR(Context* c, unsigned size, Assembler::Constant* a,
|
2008-09-07 20:12:11 +00:00
|
|
|
Assembler::Register* b)
|
|
|
|
{
|
|
|
|
|
|
|
|
int64_t v = a->value->value();
|
2010-11-30 23:58:51 +00:00
|
|
|
maybeRex(c, size, b);
|
2008-09-07 20:12:11 +00:00
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xd0 + regCode(b));
|
2008-09-07 20:12:11 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2010-11-30 23:58:51 +00:00
|
|
|
opcode(c, 0x81, 0xd0 + regCode(b));
|
|
|
|
c->code.append4(v);
|
2008-09-07 20:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
addCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
2008-09-06 21:25:41 +00:00
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
2008-09-07 20:12:11 +00:00
|
|
|
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
if (v) {
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-09-07 20:12:11 +00:00
|
|
|
ResolvedPromise high((v >> 32) & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant ah(&high);
|
|
|
|
|
|
|
|
ResolvedPromise low(v & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant al(&low);
|
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
addCR(c, 4, &al, 4, b);
|
|
|
|
addCarryCR(c, 4, &ah, &bh);
|
|
|
|
} else {
|
2009-01-04 19:32:11 +00:00
|
|
|
if (isInt32(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b);
|
2009-01-04 19:32:11 +00:00
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xc0 + regCode(b));
|
2009-01-04 19:32:11 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x81, 0xc0 + regCode(b));
|
2009-01-04 19:32:11 +00:00
|
|
|
c->code.append4(v);
|
|
|
|
}
|
2008-09-07 20:12:11 +00:00
|
|
|
} else {
|
2009-09-20 21:43:32 +00:00
|
|
|
Assembler::Register tmp
|
|
|
|
(c->client->acquireTemporary(GeneralRegisterMask));
|
2008-09-07 20:12:11 +00:00
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
addRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
subtractBorrowCR(Context* c, unsigned size UNUSED, Assembler::Constant* a,
|
|
|
|
Assembler::Register* b)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, TargetBytesPerWord == 8 or size == 4);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xd8 + regCode(b));
|
2008-09-06 21:25:41 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2012-03-11 10:58:15 +00:00
|
|
|
opcode(c, 0x81, 0xd8 + regCode(b));
|
|
|
|
c->code.append4(v);
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
subtractRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize, Assembler::Register* b);
|
|
|
|
|
|
|
|
void
|
|
|
|
subtractCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
if (v) {
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-09-06 21:25:41 +00:00
|
|
|
ResolvedPromise high((v >> 32) & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant ah(&high);
|
|
|
|
|
|
|
|
ResolvedPromise low(v & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant al(&low);
|
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
subtractCR(c, 4, &al, 4, b);
|
2008-09-07 20:12:11 +00:00
|
|
|
subtractBorrowCR(c, 4, &ah, &bh);
|
2008-09-06 21:25:41 +00:00
|
|
|
} else {
|
2009-01-04 19:32:11 +00:00
|
|
|
if (isInt32(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b);
|
2009-01-04 19:32:11 +00:00
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xe8 + regCode(b));
|
2009-01-04 19:32:11 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x81, 0xe8 + regCode(b));
|
2009-01-04 19:32:11 +00:00
|
|
|
c->code.append4(v);
|
|
|
|
}
|
2008-09-06 21:25:41 +00:00
|
|
|
} else {
|
2009-09-20 21:43:32 +00:00
|
|
|
Assembler::Register tmp
|
|
|
|
(c->client->acquireTemporary(GeneralRegisterMask));
|
2008-09-06 21:25:41 +00:00
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
subtractRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-10 22:37:21 +00:00
|
|
|
void
|
2008-09-07 20:12:11 +00:00
|
|
|
subtractBorrowRR(Context* c, unsigned size, Assembler::Register* a,
|
|
|
|
Assembler::Register* b)
|
2008-09-06 21:25:41 +00:00
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, TargetBytesPerWord == 8 or size == 4);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, size, a, b);
|
|
|
|
opcode(c, 0x19);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
subtractRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2008-09-08 02:21:52 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2008-09-06 21:25:41 +00:00
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
2009-06-11 15:40:50 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2008-09-06 21:25:41 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
subtractRR(c, 4, a, 4, b);
|
2008-09-07 20:12:11 +00:00
|
|
|
subtractBorrowRR(c, 4, &ah, &bh);
|
2008-09-06 21:25:41 +00:00
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x29);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-09-06 21:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-08 02:21:52 +00:00
|
|
|
void
|
|
|
|
andRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2008-09-08 02:21:52 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
andRR(c, 4, a, 4, b);
|
|
|
|
andRR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x21);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-09-08 02:21:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
andCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-09-08 02:21:52 +00:00
|
|
|
ResolvedPromise high((v >> 32) & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant ah(&high);
|
|
|
|
|
|
|
|
ResolvedPromise low(v & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant al(&low);
|
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
andCR(c, 4, &al, 4, b);
|
|
|
|
andCR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
|
|
|
if (isInt32(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b);
|
2008-09-08 02:21:52 +00:00
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xe0 + regCode(b));
|
2008-09-08 02:21:52 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x81, 0xe0 + regCode(b));
|
2008-09-08 02:21:52 +00:00
|
|
|
c->code.append4(v);
|
|
|
|
}
|
|
|
|
} else {
|
2009-09-20 21:43:32 +00:00
|
|
|
Assembler::Register tmp
|
|
|
|
(c->client->acquireTemporary(GeneralRegisterMask));
|
2008-09-08 02:21:52 +00:00
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
andRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-07 00:39:38 +00:00
|
|
|
void
|
|
|
|
orRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
orRR(c, 4, a, 4, b);
|
|
|
|
orRR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x09);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-11-07 00:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
orCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
if (v) {
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
ResolvedPromise high((v >> 32) & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant ah(&high);
|
|
|
|
|
|
|
|
ResolvedPromise low(v & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant al(&low);
|
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
orCR(c, 4, &al, 4, b);
|
|
|
|
orCR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
|
|
|
if (isInt32(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b);
|
2008-11-07 00:39:38 +00:00
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xc8 + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x81, 0xc8 + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
c->code.append4(v);
|
|
|
|
}
|
|
|
|
} else {
|
2009-09-20 21:43:32 +00:00
|
|
|
Assembler::Register tmp
|
|
|
|
(c->client->acquireTemporary(GeneralRegisterMask));
|
2008-11-07 00:39:38 +00:00
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
orRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
xorRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
|
|
|
{
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
xorRR(c, 4, a, 4, b);
|
|
|
|
xorRR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x31);
|
|
|
|
modrm(c, 0xc0, b, a);
|
2008-11-07 00:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
xorCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
if (v) {
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
ResolvedPromise high((v >> 32) & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant ah(&high);
|
|
|
|
|
|
|
|
ResolvedPromise low(v & 0xFFFFFFFF);
|
|
|
|
Assembler::Constant al(&low);
|
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
xorCR(c, 4, &al, 4, b);
|
|
|
|
xorCR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
|
|
|
if (isInt32(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b);
|
2008-11-07 00:39:38 +00:00
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xf0 + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x81, 0xf0 + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
c->code.append4(v);
|
|
|
|
}
|
|
|
|
} else {
|
2009-09-20 21:43:32 +00:00
|
|
|
Assembler::Register tmp
|
|
|
|
(c->client->acquireTemporary(GeneralRegisterMask));
|
2008-11-07 00:39:38 +00:00
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
xorRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
void
|
|
|
|
multiplyRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2008-11-12 01:09:45 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2008-09-09 00:31:19 +00:00
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
2009-06-11 15:40:50 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2008-09-09 00:31:19 +00:00
|
|
|
assert(c, b->high == rdx);
|
|
|
|
assert(c, b->low != rax);
|
|
|
|
assert(c, a->low != rax);
|
|
|
|
assert(c, a->high != rax);
|
|
|
|
|
|
|
|
c->client->save(rax);
|
|
|
|
|
|
|
|
Assembler::Register axdx(rax, rdx);
|
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
2010-11-30 23:58:51 +00:00
|
|
|
Assembler::Register tmp(-1);
|
|
|
|
Assembler::Register* scratch;
|
|
|
|
if (a->low == b->low) {
|
|
|
|
tmp.low = c->client->acquireTemporary
|
|
|
|
(GeneralRegisterMask & ~(1 << rax));
|
|
|
|
scratch = &tmp;
|
|
|
|
moveRR(c, 4, b, 4, scratch);
|
|
|
|
} else {
|
|
|
|
scratch = b;
|
|
|
|
}
|
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
moveRR(c, 4, b, 4, &axdx);
|
2010-11-30 23:58:51 +00:00
|
|
|
multiplyRR(c, 4, &ah, 4, scratch);
|
2008-09-09 00:31:19 +00:00
|
|
|
multiplyRR(c, 4, a, 4, &bh);
|
2010-11-30 23:58:51 +00:00
|
|
|
addRR(c, 4, &bh, 4, scratch);
|
2008-09-09 00:31:19 +00:00
|
|
|
|
|
|
|
// mul a->low,%eax%edx
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xf7, 0xe0 + a->low);
|
2008-09-09 00:31:19 +00:00
|
|
|
|
2010-11-30 23:58:51 +00:00
|
|
|
addRR(c, 4, scratch, 4, &bh);
|
2008-09-09 00:31:19 +00:00
|
|
|
moveRR(c, 4, &axdx, 4, b);
|
2010-11-30 23:58:51 +00:00
|
|
|
|
|
|
|
if (tmp.low != -1) {
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
2008-09-09 00:31:19 +00:00
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0x0f, 0xaf);
|
|
|
|
modrm(c, 0xc0, a, b);
|
2008-09-09 00:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
branch(Context* c, TernaryOperation op, Assembler::Constant* target)
|
|
|
|
{
|
|
|
|
switch (op) {
|
|
|
|
case JumpIfEqual:
|
|
|
|
conditional(c, 0x84, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfNotEqual:
|
|
|
|
conditional(c, 0x85, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfLess:
|
|
|
|
conditional(c, 0x8c, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfGreater:
|
|
|
|
conditional(c, 0x8f, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfLessOrEqual:
|
|
|
|
conditional(c, 0x8e, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfGreaterOrEqual:
|
|
|
|
conditional(c, 0x8d, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
abort(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
branchFloat(Context* c, TernaryOperation op, Assembler::Constant* target)
|
|
|
|
{
|
|
|
|
switch (op) {
|
|
|
|
case JumpIfFloatEqual:
|
|
|
|
conditional(c, 0x84, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatNotEqual:
|
|
|
|
conditional(c, 0x85, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatLess:
|
|
|
|
conditional(c, 0x82, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatGreater:
|
|
|
|
conditional(c, 0x87, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatLessOrEqual:
|
|
|
|
conditional(c, 0x86, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatGreaterOrEqual:
|
|
|
|
conditional(c, 0x83, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatLessOrUnordered:
|
|
|
|
conditional(c, 0x82, target);
|
|
|
|
conditional(c, 0x8a, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatGreaterOrUnordered:
|
|
|
|
conditional(c, 0x87, target);
|
|
|
|
conditional(c, 0x8a, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatLessOrEqualOrUnordered:
|
|
|
|
conditional(c, 0x86, target);
|
|
|
|
conditional(c, 0x8a, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfFloatGreaterOrEqualOrUnordered:
|
|
|
|
conditional(c, 0x83, target);
|
|
|
|
conditional(c, 0x8a, target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
abort(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-12 17:23:20 +00:00
|
|
|
void
|
2009-02-09 23:22:51 +00:00
|
|
|
compareRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2008-06-12 17:23:20 +00:00
|
|
|
{
|
2009-02-09 23:22:51 +00:00
|
|
|
assert(c, aSize == bSize);
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, aSize <= TargetBytesPerWord);
|
2009-06-11 15:40:50 +00:00
|
|
|
|
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x39);
|
2009-10-10 21:03:23 +00:00
|
|
|
modrm(c, 0xc0, b, a);
|
2009-02-09 23:22:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
compareCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, TargetBytesPerWord == 8 or aSize == 4);
|
2008-06-12 17:23:20 +00:00
|
|
|
|
2008-12-03 02:39:09 +00:00
|
|
|
if (a->value->resolved() and isInt32(a->value->value())) {
|
|
|
|
int64_t v = a->value->value();
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b);
|
2008-06-12 17:23:20 +00:00
|
|
|
if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x83, 0xf8 + regCode(b));
|
2008-06-12 17:23:20 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x81, 0xf8 + regCode(b));
|
2008-06-12 17:23:20 +00:00
|
|
|
c->code.append4(v);
|
|
|
|
}
|
|
|
|
} else {
|
2009-08-06 16:26:22 +00:00
|
|
|
Assembler::Register tmp(c->client->acquireTemporary(GeneralRegisterMask));
|
2009-02-09 23:22:51 +00:00
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
compareRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
2009-02-09 23:22:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-12 17:23:20 +00:00
|
|
|
void
|
2009-02-09 23:22:01 +00:00
|
|
|
compareRM(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Memory* b)
|
2008-06-12 17:23:20 +00:00
|
|
|
{
|
2009-02-09 23:22:01 +00:00
|
|
|
assert(c, aSize == bSize);
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, TargetBytesPerWord == 8 or aSize == 4);
|
2008-06-12 17:23:20 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8 and aSize == 4) {
|
2009-02-09 23:22:01 +00:00
|
|
|
moveRR(c, 4, a, 8, a);
|
2008-06-12 17:23:20 +00:00
|
|
|
}
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0x39);
|
|
|
|
modrmSibImm(c, a, b);
|
2008-06-12 17:23:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-02-09 23:22:01 +00:00
|
|
|
compareCM(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Memory* b)
|
2008-06-12 17:23:20 +00:00
|
|
|
{
|
2009-02-09 23:22:01 +00:00
|
|
|
assert(c, aSize == bSize);
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, TargetBytesPerWord == 8 or aSize == 4);
|
2008-06-12 17:23:20 +00:00
|
|
|
|
2009-02-09 23:22:51 +00:00
|
|
|
if (a->value->resolved()) {
|
|
|
|
int64_t v = a->value->value();
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, aSize, b);
|
|
|
|
opcode(c, isInt8(v) ? 0x83 : 0x81);
|
|
|
|
modrmSibImm(c, rdi, b->scale, b->index, b->base, b->offset);
|
2008-12-03 02:39:09 +00:00
|
|
|
|
|
|
|
if (isInt8(v)) {
|
|
|
|
c->code.append(v);
|
|
|
|
} else if (isInt32(v)) {
|
|
|
|
c->code.append4(v);
|
|
|
|
} else {
|
|
|
|
abort(c);
|
|
|
|
}
|
|
|
|
} else {
|
2009-08-06 16:26:22 +00:00
|
|
|
Assembler::Register tmp(c->client->acquireTemporary(GeneralRegisterMask));
|
2009-02-09 23:22:01 +00:00
|
|
|
moveCR(c, aSize, a, bSize, &tmp);
|
|
|
|
compareRM(c, bSize, &tmp, bSize, b);
|
2008-12-03 02:39:09 +00:00
|
|
|
c->client->releaseTemporary(tmp.low);
|
2008-06-12 17:23:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
void
|
2009-10-10 21:03:23 +00:00
|
|
|
compareFloatRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2008-03-10 22:37:21 +00:00
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
assert(c, aSize == bSize);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
if (aSize == 8) {
|
|
|
|
opcode(c, 0x66);
|
|
|
|
}
|
|
|
|
maybeRex(c, 4, a, b);
|
|
|
|
opcode(c, 0x0f, 0x2e);
|
|
|
|
modrm(c, 0xc0, a, b);
|
|
|
|
}
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
branchLong(Context* c, TernaryOperation op, Assembler::Operand* al,
|
|
|
|
Assembler::Operand* ah, Assembler::Operand* bl,
|
|
|
|
Assembler::Operand* bh, Assembler::Constant* target,
|
|
|
|
BinaryOperationType compare)
|
|
|
|
{
|
|
|
|
compare(c, 4, ah, 4, bh);
|
|
|
|
|
|
|
|
unsigned next = 0;
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
switch (op) {
|
|
|
|
case JumpIfEqual:
|
|
|
|
opcode(c, 0x75); // jne
|
|
|
|
next = c->code.length();
|
|
|
|
c->code.append(0);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
compare(c, 4, al, 4, bl);
|
|
|
|
conditional(c, 0x84, target); // je
|
|
|
|
break;
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
case JumpIfNotEqual:
|
|
|
|
conditional(c, 0x85, target); // jne
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
compare(c, 4, al, 4, bl);
|
|
|
|
conditional(c, 0x85, target); // jne
|
|
|
|
break;
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
case JumpIfLess:
|
|
|
|
conditional(c, 0x8c, target); // jl
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x7f); // jg
|
|
|
|
next = c->code.length();
|
|
|
|
c->code.append(0);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
compare(c, 4, al, 4, bl);
|
|
|
|
conditional(c, 0x82, target); // jb
|
|
|
|
break;
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
case JumpIfGreater:
|
|
|
|
conditional(c, 0x8f, target); // jg
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x7c); // jl
|
|
|
|
next = c->code.length();
|
|
|
|
c->code.append(0);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
compare(c, 4, al, 4, bl);
|
|
|
|
conditional(c, 0x87, target); // ja
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JumpIfLessOrEqual:
|
|
|
|
conditional(c, 0x8c, target); // jl
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x7f); // jg
|
|
|
|
next = c->code.length();
|
|
|
|
c->code.append(0);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
|
|
|
compare(c, 4, al, 4, bl);
|
2009-10-10 21:03:23 +00:00
|
|
|
conditional(c, 0x86, target); // jbe
|
|
|
|
break;
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
case JumpIfGreaterOrEqual:
|
|
|
|
conditional(c, 0x8f, target); // jg
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x7c); // jl
|
|
|
|
next = c->code.length();
|
|
|
|
c->code.append(0);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
compare(c, 4, al, 4, bl);
|
|
|
|
conditional(c, 0x83, target); // jae
|
|
|
|
break;
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
default:
|
|
|
|
abort(c);
|
|
|
|
}
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
if (next) {
|
|
|
|
int8_t nextOffset = c->code.length() - next - 1;
|
|
|
|
c->code.set(next, &nextOffset, 1);
|
|
|
|
}
|
|
|
|
}
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
branchRR(Context* c, TernaryOperation op, unsigned size,
|
|
|
|
Assembler::Register* a, Assembler::Register* b,
|
|
|
|
Assembler::Constant* target)
|
|
|
|
{
|
|
|
|
if (isFloatBranch(op)) {
|
|
|
|
compareFloatRR(c, size, a, size, b);
|
|
|
|
branchFloat(c, op, target);
|
2011-08-30 01:00:17 +00:00
|
|
|
} else if (size > TargetBytesPerWord) {
|
2009-10-10 21:03:23 +00:00
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
branchLong(c, op, a, &ah, b, &bh, target, CAST2(compareRR));
|
|
|
|
} else {
|
|
|
|
compareRR(c, size, a, size, b);
|
|
|
|
branch(c, op, target);
|
|
|
|
}
|
|
|
|
}
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
branchCR(Context* c, TernaryOperation op, unsigned size,
|
|
|
|
Assembler::Constant* a, Assembler::Register* b,
|
|
|
|
Assembler::Constant* target)
|
|
|
|
{
|
|
|
|
assert(c, not isFloatBranch(op));
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (size > TargetBytesPerWord) {
|
2009-10-10 21:03:23 +00:00
|
|
|
int64_t v = a->value->value();
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
ResolvedPromise low(v & ~static_cast<uintptr_t>(0));
|
|
|
|
Assembler::Constant al(&low);
|
|
|
|
|
|
|
|
ResolvedPromise high((v >> 32) & ~static_cast<uintptr_t>(0));
|
|
|
|
Assembler::Constant ah(&high);
|
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
branchLong(c, op, &al, &ah, b, &bh, target, CAST2(compareCR));
|
|
|
|
} else {
|
|
|
|
compareCR(c, size, a, size, b);
|
|
|
|
branch(c, op, target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
branchRM(Context* c, TernaryOperation op, unsigned size,
|
|
|
|
Assembler::Register* a, Assembler::Memory* b,
|
|
|
|
Assembler::Constant* target)
|
|
|
|
{
|
|
|
|
assert(c, not isFloatBranch(op));
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size <= TargetBytesPerWord);
|
2009-10-10 21:03:23 +00:00
|
|
|
|
|
|
|
compareRM(c, size, a, size, b);
|
|
|
|
branch(c, op, target);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
branchCM(Context* c, TernaryOperation op, unsigned size,
|
|
|
|
Assembler::Constant* a, Assembler::Memory* b,
|
|
|
|
Assembler::Constant* target)
|
|
|
|
{
|
|
|
|
assert(c, not isFloatBranch(op));
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(c, size <= TargetBytesPerWord);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
compareCM(c, size, a, size, b);
|
|
|
|
branch(c, op, target);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
multiplyCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2009-10-10 21:03:23 +00:00
|
|
|
const uint32_t mask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx));
|
|
|
|
Assembler::Register tmp(c->client->acquireTemporary(mask),
|
|
|
|
c->client->acquireTemporary(mask));
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
multiplyRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
c->client->releaseTemporary(tmp.high);
|
|
|
|
} else {
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
if (v != 1) {
|
|
|
|
if (isInt32(v)) {
|
|
|
|
maybeRex(c, bSize, b, b);
|
|
|
|
if (isInt8(v)) {
|
|
|
|
opcode(c, 0x6b);
|
|
|
|
modrm(c, 0xc0, b, b);
|
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
|
|
|
opcode(c, 0x69);
|
|
|
|
modrm(c, 0xc0, b, b);
|
|
|
|
c->code.append4(v);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Assembler::Register tmp
|
|
|
|
(c->client->acquireTemporary(GeneralRegisterMask));
|
|
|
|
moveCR(c, aSize, a, aSize, &tmp);
|
|
|
|
multiplyRR(c, aSize, &tmp, bSize, b);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
}
|
|
|
|
}
|
2008-10-15 00:45:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
divideRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2008-11-12 01:09:45 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b UNUSED)
|
2008-10-15 00:45:31 +00:00
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
|
|
|
assert(c, b->low == rax);
|
|
|
|
assert(c, a->low != rdx);
|
|
|
|
|
|
|
|
c->client->save(rdx);
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
|
|
|
|
maybeRex(c, aSize, a, b);
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x99); // cdq
|
|
|
|
maybeRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0xf7, 0xf8 + regCode(a));
|
2008-10-15 00:45:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
remainderRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2008-11-12 01:09:45 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2008-10-15 00:45:31 +00:00
|
|
|
{
|
|
|
|
assert(c, aSize == bSize);
|
|
|
|
|
|
|
|
assert(c, b->low == rax);
|
|
|
|
assert(c, a->low != rdx);
|
|
|
|
|
|
|
|
c->client->save(rdx);
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
|
|
|
|
maybeRex(c, aSize, a, b);
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x99); // cdq
|
|
|
|
maybeRex(c, aSize, b, a);
|
|
|
|
opcode(c, 0xf7, 0xf8 + regCode(a));
|
2008-10-15 00:45:31 +00:00
|
|
|
|
|
|
|
Assembler::Register dx(rdx);
|
2011-08-30 01:00:17 +00:00
|
|
|
moveRR(c, TargetBytesPerWord, &dx, TargetBytesPerWord, b);
|
2008-10-15 00:45:31 +00:00
|
|
|
}
|
|
|
|
|
2008-11-07 00:39:38 +00:00
|
|
|
void
|
2009-06-11 15:40:50 +00:00
|
|
|
doShift(Context* c, UNUSED void (*shift)
|
2008-11-07 00:39:38 +00:00
|
|
|
(Context*, unsigned, Assembler::Register*, unsigned,
|
|
|
|
Assembler::Register*),
|
2009-06-11 15:40:50 +00:00
|
|
|
int type, UNUSED unsigned aSize, Assembler::Constant* a,
|
2008-11-07 00:39:38 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
int64_t v = a->value->value();
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
c->client->save(rcx);
|
|
|
|
|
|
|
|
Assembler::Register cx(rcx);
|
|
|
|
moveCR(c, 4, a, 4, &cx);
|
|
|
|
shift(c, aSize, &cx, bSize, b);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, b);
|
2008-11-07 00:39:38 +00:00
|
|
|
if (v == 1) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xd1, type + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
} else if (isInt8(v)) {
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xc1, type + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
c->code.append(v);
|
|
|
|
} else {
|
|
|
|
abort(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-11 15:40:50 +00:00
|
|
|
shiftLeftRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a,
|
2008-11-07 00:39:38 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, a->low == rcx);
|
2009-06-11 15:40:50 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
// shld
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x0f, 0xa5);
|
|
|
|
modrm(c, 0xc0, b->high, b->low);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
// shl
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xd3, 0xe0 + b->low);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
ResolvedPromise promise(32);
|
|
|
|
Assembler::Constant constant(&promise);
|
|
|
|
compareCR(c, aSize, &constant, aSize, a);
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x7c); //jl
|
|
|
|
c->code.append(2 + 2);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
moveRR(c, 4, b, 4, &bh); // 2 bytes
|
|
|
|
xorRR(c, 4, b, 4, b); // 2 bytes
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0xd3, 0xe0 + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
shiftLeftCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
doShift(c, shiftLeftRR, 0xe0, aSize, a, bSize, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-11 15:40:50 +00:00
|
|
|
shiftRightRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a,
|
2008-11-07 00:39:38 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, a->low == rcx);
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
// shrd
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x0f, 0xad);
|
|
|
|
modrm(c, 0xc0, b->low, b->high);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
// sar
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xd3, 0xf8 + b->high);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
ResolvedPromise promise(32);
|
|
|
|
Assembler::Constant constant(&promise);
|
|
|
|
compareCR(c, aSize, &constant, aSize, a);
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x7c); //jl
|
|
|
|
c->code.append(2 + 3);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
moveRR(c, 4, &bh, 4, b); // 2 bytes
|
|
|
|
|
|
|
|
// sar 31,high
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xc1, 0xf8 + b->high);
|
2008-11-07 00:39:38 +00:00
|
|
|
c->code.append(31);
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0xd3, 0xf8 + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
shiftRightCR(Context* c, unsigned aSize, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
doShift(c, shiftRightRR, 0xf8, aSize, a, bSize, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-11 15:40:50 +00:00
|
|
|
unsignedShiftRightRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a,
|
2008-11-07 00:39:38 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
assert(c, a->low == rcx);
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and bSize == 8) {
|
2008-11-07 00:39:38 +00:00
|
|
|
// shrd
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0x0f, 0xad);
|
|
|
|
modrm(c, 0xc0, b->low, b->high);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
// shr
|
2009-06-11 15:40:50 +00:00
|
|
|
opcode(c, 0xd3, 0xe8 + b->high);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
ResolvedPromise promise(32);
|
|
|
|
Assembler::Constant constant(&promise);
|
|
|
|
compareCR(c, aSize, &constant, aSize, a);
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
opcode(c, 0x7c); //jl
|
|
|
|
c->code.append(2 + 2);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
moveRR(c, 4, &bh, 4, b); // 2 bytes
|
|
|
|
xorRR(c, 4, &bh, 4, &bh); // 2 bytes
|
|
|
|
} else {
|
2009-06-11 15:40:50 +00:00
|
|
|
maybeRex(c, bSize, a, b);
|
|
|
|
opcode(c, 0xd3, 0xe8 + regCode(b));
|
2008-11-07 00:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
unsignedShiftRightCR(Context* c, unsigned aSize UNUSED, Assembler::Constant* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
doShift(c, unsignedShiftRightRR, 0xe8, aSize, a, bSize, b);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
floatRegOp(Context* c, unsigned aSize, Assembler::Register* a, unsigned bSize,
|
|
|
|
Assembler::Register* b, uint8_t op, uint8_t mod = 0xc0)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
if (aSize == 4) {
|
2009-08-06 16:26:22 +00:00
|
|
|
opcode(c, 0xf3);
|
|
|
|
} else {
|
|
|
|
opcode(c, 0xf2);
|
|
|
|
}
|
2009-10-05 14:25:12 +00:00
|
|
|
maybeRex(c, bSize, b, a);
|
2009-08-06 16:26:22 +00:00
|
|
|
opcode(c, 0x0f, op);
|
|
|
|
modrm(c, mod, a, b);
|
|
|
|
}
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
void
|
|
|
|
floatMemOp(Context* c, unsigned aSize, Assembler::Memory* a, unsigned bSize,
|
|
|
|
Assembler::Register* b, uint8_t op)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
if (aSize == 4) {
|
2009-08-06 16:26:22 +00:00
|
|
|
opcode(c, 0xf3);
|
|
|
|
} else {
|
|
|
|
opcode(c, 0xf2);
|
|
|
|
}
|
|
|
|
maybeRex(c, bSize, b, a);
|
|
|
|
opcode(c, 0x0f, op);
|
|
|
|
modrmSibImm(c, b, a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatSqrtRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatRegOp(c, aSize, a, 4, b, 0x51);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatSqrtMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatMemOp(c, aSize, a, 4, b, 0x51);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatAddRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatRegOp(c, aSize, a, 4, b, 0x58);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatAddMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatMemOp(c, aSize, a, 4, b, 0x58);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatSubtractRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatRegOp(c, aSize, a, 4, b, 0x5c);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatSubtractMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatMemOp(c, aSize, a, 4, b, 0x5c);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatMultiplyRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatRegOp(c, aSize, a, 4, b, 0x59);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatMultiplyMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatMemOp(c, aSize, a, 4, b, 0x59);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatDivideRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatRegOp(c, aSize, a, 4, b, 0x5e);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatDivideMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatMemOp(c, aSize, a, 4, b, 0x5e);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
float2FloatRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatRegOp(c, aSize, a, 4, b, 0x5a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
float2FloatMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatMemOp(c, aSize, a, 4, b, 0x5a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
float2IntRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-10-10 23:46:43 +00:00
|
|
|
assert(c, not floatReg(b));
|
2009-10-20 21:43:24 +00:00
|
|
|
floatRegOp(c, aSize, a, bSize, b, 0x2c);
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
float2IntMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-10-20 21:43:24 +00:00
|
|
|
floatMemOp(c, aSize, a, bSize, b, 0x2c);
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
int2FloatRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatRegOp(c, bSize, a, aSize, b, 0x2a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
int2FloatMR(Context* c, unsigned aSize, Assembler::Memory* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
floatMemOp(c, bSize, a, aSize, b, 0x2a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
floatNegateRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
assert(c, floatReg(a) and floatReg(b));
|
2009-09-20 21:43:32 +00:00
|
|
|
// unlike most of the other floating point code, this does NOT
|
|
|
|
// support doubles:
|
|
|
|
assert(c, aSize == 4);
|
2009-08-06 16:26:22 +00:00
|
|
|
ResolvedPromise pcon(0x80000000);
|
|
|
|
Assembler::Constant con(&pcon);
|
2009-10-10 23:46:43 +00:00
|
|
|
if (a->low == b->low) {
|
2009-08-06 16:26:22 +00:00
|
|
|
Assembler::Register tmp(c->client->acquireTemporary(FloatRegisterMask));
|
|
|
|
moveCR(c, 4, &con, 4, &tmp);
|
|
|
|
maybeRex(c, 4, a, &tmp);
|
|
|
|
opcode(c, 0x0f, 0x57);
|
|
|
|
modrm(c, 0xc0, &tmp, a);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
} else {
|
|
|
|
moveCR(c, 4, &con, 4, b);
|
2009-10-10 23:46:43 +00:00
|
|
|
if (aSize == 8) opcode(c, 0x66);
|
2009-08-06 16:26:22 +00:00
|
|
|
maybeRex(c, 4, a, b);
|
|
|
|
opcode(c, 0x0f, 0x57);
|
|
|
|
modrm(c, 0xc0, a, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-10-18 01:26:14 +00:00
|
|
|
floatAbsoluteRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a,
|
2009-10-10 21:03:23 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
|
|
|
assert(c, floatReg(a) and floatReg(b));
|
2009-09-20 21:43:32 +00:00
|
|
|
// unlike most of the other floating point code, this does NOT
|
|
|
|
// support doubles:
|
|
|
|
assert(c, aSize == 4);
|
2009-08-06 16:26:22 +00:00
|
|
|
ResolvedPromise pcon(0x7fffffff);
|
|
|
|
Assembler::Constant con(&pcon);
|
2009-10-10 23:46:43 +00:00
|
|
|
if (a->low == b->low) {
|
2009-08-06 16:26:22 +00:00
|
|
|
Assembler::Register tmp(c->client->acquireTemporary(FloatRegisterMask));
|
|
|
|
moveCR(c, 4, &con, 4, &tmp);
|
|
|
|
maybeRex(c, 4, a, &tmp);
|
|
|
|
opcode(c, 0x0f, 0x54);
|
|
|
|
modrm(c, 0xc0, &tmp, a);
|
|
|
|
c->client->releaseTemporary(tmp.low);
|
|
|
|
} else {
|
|
|
|
moveCR(c, 4, &con, 4, b);
|
|
|
|
maybeRex(c, 4, a, b);
|
|
|
|
opcode(c, 0x0f, 0x54);
|
|
|
|
modrm(c, 0xc0, a, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-10-18 01:26:14 +00:00
|
|
|
absoluteRR(Context* c, unsigned aSize, Assembler::Register* a,
|
2009-08-06 16:26:22 +00:00
|
|
|
unsigned bSize UNUSED, Assembler::Register* b UNUSED)
|
|
|
|
{
|
|
|
|
assert(c, aSize == bSize and a->low == rax and b->low == rax);
|
2009-10-10 21:03:23 +00:00
|
|
|
Assembler::Register d
|
|
|
|
(c->client->acquireTemporary(static_cast<uint64_t>(1) << rdx));
|
2009-08-06 16:26:22 +00:00
|
|
|
maybeRex(c, aSize, a, b);
|
|
|
|
opcode(c, 0x99);
|
|
|
|
xorRR(c, aSize, &d, aSize, a);
|
|
|
|
subtractRR(c, aSize, &d, aSize, a);
|
|
|
|
c->client->releaseTemporary(rdx);
|
|
|
|
}
|
|
|
|
|
2011-01-26 00:22:43 +00:00
|
|
|
unsigned
|
|
|
|
argumentFootprint(unsigned footprint)
|
|
|
|
{
|
|
|
|
return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
read4(uint8_t* p)
|
|
|
|
{
|
|
|
|
uint32_t v; memcpy(&v, p, 4);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-01-28 15:43:11 +00:00
|
|
|
nextFrame(ArchitectureContext* c UNUSED, uint8_t* start, unsigned size UNUSED,
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
unsigned footprint, void*, bool mostRecent,
|
2011-01-26 00:22:43 +00:00
|
|
|
unsigned targetParameterFootprint, void** ip, void** stack)
|
|
|
|
{
|
|
|
|
assert(c, *ip >= start);
|
|
|
|
assert(c, *ip <= start + size);
|
|
|
|
|
|
|
|
uint8_t* instruction = static_cast<uint8_t*>(*ip);
|
|
|
|
|
2011-01-29 00:15:57 +00:00
|
|
|
// skip stack overflow check, if present:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4) {
|
2011-01-26 00:22:43 +00:00
|
|
|
if (*start == 0x39) {
|
|
|
|
start += 11;
|
|
|
|
}
|
|
|
|
} else if (*start == 0x48 and start[1] == 0x39) {
|
|
|
|
start += 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (instruction <= start) {
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
assert(c, mostRecent);
|
2011-01-30 01:07:52 +00:00
|
|
|
*ip = static_cast<void**>(*stack)[0];
|
2011-01-26 00:22:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UseFramePointer) {
|
|
|
|
// skip preamble
|
2011-08-30 01:00:17 +00:00
|
|
|
start += (TargetBytesPerWord == 4 ? 3 : 4);
|
2011-01-26 00:22:43 +00:00
|
|
|
|
|
|
|
if (instruction <= start or *instruction == 0x5d) {
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
assert(c, mostRecent);
|
|
|
|
|
2011-01-30 01:07:52 +00:00
|
|
|
*ip = static_cast<void**>(*stack)[1];
|
|
|
|
*stack = static_cast<void**>(*stack) + 1;
|
2011-01-26 00:22:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-29 00:15:57 +00:00
|
|
|
if (*instruction == 0xc3) { // return
|
2011-01-30 01:07:52 +00:00
|
|
|
*ip = static_cast<void**>(*stack)[0];
|
2011-01-26 00:22:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
unsigned offset = footprint + FrameHeaderSize - (mostRecent ? 1 : 0);
|
2011-01-26 00:22:43 +00:00
|
|
|
|
|
|
|
if (TailCalls) {
|
|
|
|
if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) {
|
|
|
|
offset += argumentFootprint(targetParameterFootprint)
|
|
|
|
- StackAlignmentInWords;
|
|
|
|
}
|
|
|
|
|
2011-01-29 00:15:57 +00:00
|
|
|
// check for post-non-tail-call stack adjustment of the form "add
|
|
|
|
// $offset,%rsp":
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4) {
|
2011-01-26 00:22:43 +00:00
|
|
|
if ((*instruction == 0x83 or *instruction == 0x81)
|
|
|
|
and instruction[1] == 0xec)
|
|
|
|
{
|
|
|
|
offset
|
|
|
|
-= (*instruction == 0x83 ? instruction[2] : read4(instruction + 2))
|
2011-08-30 01:00:17 +00:00
|
|
|
/ TargetBytesPerWord;
|
2011-01-26 00:22:43 +00:00
|
|
|
}
|
|
|
|
} else if (*instruction == 0x48
|
|
|
|
and (instruction[1] == 0x83 or instruction[1] == 0x81)
|
|
|
|
and instruction[2] == 0xec)
|
|
|
|
{
|
|
|
|
offset
|
|
|
|
-= (instruction[1] == 0x83 ? instruction[3] : read4(instruction + 3))
|
2011-08-30 01:00:17 +00:00
|
|
|
/ TargetBytesPerWord;
|
2011-01-26 00:22:43 +00:00
|
|
|
}
|
|
|
|
|
2011-01-30 21:14:57 +00:00
|
|
|
// todo: check for and handle tail calls
|
2011-01-26 00:22:43 +00:00
|
|
|
}
|
|
|
|
|
2011-01-30 01:07:52 +00:00
|
|
|
*ip = static_cast<void**>(*stack)[offset];
|
|
|
|
*stack = static_cast<void**>(*stack) + offset;
|
2011-01-26 00:22:43 +00:00
|
|
|
}
|
|
|
|
|
2008-10-15 00:45:31 +00:00
|
|
|
void
|
|
|
|
populateTables(ArchitectureContext* c)
|
|
|
|
{
|
2008-09-06 21:25:41 +00:00
|
|
|
const OperandType C = ConstantOperand;
|
2008-09-08 02:21:52 +00:00
|
|
|
const OperandType A = AddressOperand;
|
2008-09-06 21:25:41 +00:00
|
|
|
const OperandType R = RegisterOperand;
|
|
|
|
const OperandType M = MemoryOperand;
|
|
|
|
|
|
|
|
OperationType* zo = c->operations;
|
|
|
|
UnaryOperationType* uo = c->unaryOperations;
|
|
|
|
BinaryOperationType* bo = c->binaryOperations;
|
2009-10-10 21:03:23 +00:00
|
|
|
BranchOperationType* bro = c->branchOperations;
|
2008-03-10 22:37:21 +00:00
|
|
|
|
2008-09-06 21:25:41 +00:00
|
|
|
zo[Return] = return_;
|
2009-03-03 03:18:15 +00:00
|
|
|
zo[LoadBarrier] = ignore;
|
|
|
|
zo[StoreStoreBarrier] = ignore;
|
2009-11-30 15:38:16 +00:00
|
|
|
zo[StoreLoadBarrier] = storeLoadBarrier;
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
zo[Trap] = trap;
|
2008-02-17 22:29:04 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
uo[index(c, Call, C)] = CAST1(callC);
|
|
|
|
uo[index(c, Call, R)] = CAST1(callR);
|
|
|
|
uo[index(c, Call, M)] = CAST1(callM);
|
2008-09-08 02:21:52 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
uo[index(c, AlignedCall, C)] = CAST1(alignedCallC);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
uo[index(c, LongCall, C)] = CAST1(longCallC);
|
2008-09-09 00:31:19 +00:00
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
uo[index(c, AlignedLongCall, C)] = CAST1(alignedLongCallC);
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
uo[index(c, Jump, R)] = CAST1(jumpR);
|
|
|
|
uo[index(c, Jump, C)] = CAST1(jumpC);
|
|
|
|
uo[index(c, Jump, M)] = CAST1(jumpM);
|
2009-04-22 01:39:25 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
uo[index(c, AlignedJump, C)] = CAST1(alignedJumpC);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
uo[index(c, LongJump, C)] = CAST1(longJumpC);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
uo[index(c, AlignedLongJump, C)] = CAST1(alignedLongJumpC);
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Negate, R, R)] = CAST2(negateRR);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, FloatNegate, R, R)] = CAST2(floatNegateRR);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Move, R, R)] = CAST2(moveRR);
|
|
|
|
bo[index(c, Move, C, R)] = CAST2(moveCR);
|
|
|
|
bo[index(c, Move, M, R)] = CAST2(moveMR);
|
|
|
|
bo[index(c, Move, R, M)] = CAST2(moveRM);
|
|
|
|
bo[index(c, Move, C, M)] = CAST2(moveCM);
|
|
|
|
bo[index(c, Move, A, R)] = CAST2(moveAR);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-18 01:26:14 +00:00
|
|
|
bo[index(c, FloatSquareRoot, R, R)] = CAST2(floatSqrtRR);
|
|
|
|
bo[index(c, FloatSquareRoot, M, R)] = CAST2(floatSqrtMR);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, MoveZ, R, R)] = CAST2(moveZRR);
|
|
|
|
bo[index(c, MoveZ, M, R)] = CAST2(moveZMR);
|
2012-03-13 23:00:47 +00:00
|
|
|
bo[index(c, MoveZ, C, R)] = CAST2(moveCR);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Add, R, R)] = CAST2(addRR);
|
|
|
|
bo[index(c, Add, C, R)] = CAST2(addCR);
|
2008-09-27 22:00:58 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Subtract, C, R)] = CAST2(subtractCR);
|
|
|
|
bo[index(c, Subtract, R, R)] = CAST2(subtractRR);
|
2008-09-09 00:31:19 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, FloatAdd, R, R)] = CAST2(floatAddRR);
|
|
|
|
bo[index(c, FloatAdd, M, R)] = CAST2(floatAddMR);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, FloatSubtract, R, R)] = CAST2(floatSubtractRR);
|
|
|
|
bo[index(c, FloatSubtract, M, R)] = CAST2(floatSubtractMR);
|
2008-09-07 20:12:11 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, And, R, R)] = CAST2(andRR);
|
|
|
|
bo[index(c, And, C, R)] = CAST2(andCR);
|
2008-09-08 02:21:52 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Or, R, R)] = CAST2(orRR);
|
|
|
|
bo[index(c, Or, C, R)] = CAST2(orCR);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Xor, R, R)] = CAST2(xorRR);
|
|
|
|
bo[index(c, Xor, C, R)] = CAST2(xorCR);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Multiply, R, R)] = CAST2(multiplyRR);
|
|
|
|
bo[index(c, Multiply, C, R)] = CAST2(multiplyCR);
|
2008-09-09 00:31:19 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Divide, R, R)] = CAST2(divideRR);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, FloatMultiply, R, R)] = CAST2(floatMultiplyRR);
|
|
|
|
bo[index(c, FloatMultiply, M, R)] = CAST2(floatMultiplyMR);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, FloatDivide, R, R)] = CAST2(floatDivideRR);
|
|
|
|
bo[index(c, FloatDivide, M, R)] = CAST2(floatDivideMR);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Remainder, R, R)] = CAST2(remainderRR);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, ShiftLeft, R, R)] = CAST2(shiftLeftRR);
|
|
|
|
bo[index(c, ShiftLeft, C, R)] = CAST2(shiftLeftCR);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, ShiftRight, R, R)] = CAST2(shiftRightRR);
|
|
|
|
bo[index(c, ShiftRight, C, R)] = CAST2(shiftRightCR);
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, UnsignedShiftRight, R, R)] = CAST2(unsignedShiftRightRR);
|
|
|
|
bo[index(c, UnsignedShiftRight, C, R)] = CAST2(unsignedShiftRightCR);
|
2008-10-15 00:45:31 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Float2Float, R, R)] = CAST2(float2FloatRR);
|
|
|
|
bo[index(c, Float2Float, M, R)] = CAST2(float2FloatMR);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Float2Int, R, R)] = CAST2(float2IntRR);
|
|
|
|
bo[index(c, Float2Int, M, R)] = CAST2(float2IntMR);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bo[index(c, Int2Float, R, R)] = CAST2(int2FloatRR);
|
|
|
|
bo[index(c, Int2Float, M, R)] = CAST2(int2FloatMR);
|
2008-11-07 00:39:38 +00:00
|
|
|
|
2009-10-18 01:26:14 +00:00
|
|
|
bo[index(c, Absolute, R, R)] = CAST2(absoluteRR);
|
|
|
|
bo[index(c, FloatAbsolute, R, R)] = CAST2(floatAbsoluteRR);
|
2008-02-17 22:29:04 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
bro[branchIndex(c, R, R)] = CAST_BRANCH(branchRR);
|
|
|
|
bro[branchIndex(c, C, R)] = CAST_BRANCH(branchCR);
|
|
|
|
bro[branchIndex(c, C, M)] = CAST_BRANCH(branchCM);
|
|
|
|
bro[branchIndex(c, R, M)] = CAST_BRANCH(branchRM);
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
2009-10-10 23:46:43 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
class MyArchitecture: public Assembler::Architecture {
|
|
|
|
public:
|
2009-10-10 23:46:43 +00:00
|
|
|
MyArchitecture(System* system, bool useNativeFeatures):
|
|
|
|
c(system, useNativeFeatures), referenceCount(0)
|
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
populateTables(&c);
|
|
|
|
}
|
2008-06-02 13:49:09 +00:00
|
|
|
|
2009-09-26 19:43:44 +00:00
|
|
|
virtual unsigned floatRegisterSize() {
|
2009-10-10 23:46:43 +00:00
|
|
|
if (useSSE(&c)) {
|
2009-09-26 19:43:44 +00:00
|
|
|
return 8;
|
2009-08-06 16:26:22 +00:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-26 19:43:44 +00:00
|
|
|
virtual uint32_t generalRegisterMask() {
|
|
|
|
return GeneralRegisterMask;
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
|
2009-09-26 19:43:44 +00:00
|
|
|
virtual uint32_t floatRegisterMask() {
|
2009-10-10 23:46:43 +00:00
|
|
|
return useSSE(&c) ? FloatRegisterMask : 0;
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-06-02 13:49:09 +00:00
|
|
|
|
2011-09-24 04:21:54 +00:00
|
|
|
virtual int scratch() {
|
|
|
|
return rax;
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int stack() {
|
|
|
|
return rsp;
|
2008-05-31 22:14:27 +00:00
|
|
|
}
|
2008-03-13 20:50:56 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int thread() {
|
|
|
|
return rbx;
|
|
|
|
}
|
2008-04-20 19:35:36 +00:00
|
|
|
|
2009-02-28 21:20:43 +00:00
|
|
|
virtual int returnLow() {
|
2008-08-19 23:38:37 +00:00
|
|
|
return rax;
|
|
|
|
}
|
2008-04-20 19:35:36 +00:00
|
|
|
|
2009-02-18 01:19:31 +00:00
|
|
|
virtual int returnHigh() {
|
2011-08-30 01:00:17 +00:00
|
|
|
return (TargetBytesPerWord == 4 ? rdx : NoRegister);
|
2009-02-18 01:19:31 +00:00
|
|
|
}
|
|
|
|
|
2009-05-03 20:57:11 +00:00
|
|
|
virtual int virtualCallTarget() {
|
2009-04-19 22:36:11 +00:00
|
|
|
return rax;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual int virtualCallIndex() {
|
|
|
|
return rdx;
|
|
|
|
}
|
|
|
|
|
2009-03-01 19:28:17 +00:00
|
|
|
virtual bool bigEndian() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
virtual uintptr_t maximumImmediateJump() {
|
|
|
|
return 0x7FFFFFFF;
|
|
|
|
}
|
|
|
|
|
2008-08-23 18:04:36 +00:00
|
|
|
virtual bool reserved(int register_) {
|
|
|
|
switch (register_) {
|
|
|
|
case rbp:
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
return UseFramePointer;
|
|
|
|
|
2008-08-23 18:04:36 +00:00
|
|
|
case rsp:
|
|
|
|
case rbx:
|
|
|
|
return true;
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2008-08-23 18:04:36 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-25 17:49:56 +00:00
|
|
|
virtual unsigned frameFootprint(unsigned footprint) {
|
2012-05-01 19:16:32 +00:00
|
|
|
#if AVIAN_TARGET_PLATFORM == AVIAN_PLATFORM_WINDOWS
|
2009-07-26 02:48:36 +00:00
|
|
|
return max(footprint, StackAlignmentInWords);
|
|
|
|
#else
|
2009-04-19 22:36:11 +00:00
|
|
|
return max(footprint > argumentRegisterCount() ?
|
|
|
|
footprint - argumentRegisterCount() : 0,
|
|
|
|
StackAlignmentInWords);
|
2009-07-26 02:48:36 +00:00
|
|
|
#endif
|
2009-04-19 22:36:11 +00:00
|
|
|
}
|
|
|
|
|
2009-02-26 03:49:42 +00:00
|
|
|
virtual unsigned argumentFootprint(unsigned footprint) {
|
2011-01-26 00:22:43 +00:00
|
|
|
return local::argumentFootprint(footprint);
|
2009-02-26 03:49:42 +00:00
|
|
|
}
|
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
virtual bool argumentAlignment() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-08-11 03:33:56 +00:00
|
|
|
virtual bool argumentRegisterAlignment() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned argumentRegisterCount() {
|
2012-05-01 19:16:32 +00:00
|
|
|
#if AVIAN_TARGET_PLATFORM == AVIAN_PLATFORM_WINDOWS
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) return 4; else
|
2009-07-27 13:49:54 +00:00
|
|
|
#else
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 8) return 6; else
|
2009-06-11 15:40:50 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-03-13 23:43:11 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int argumentRegister(unsigned index) {
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(&c, TargetBytesPerWord == 8);
|
2008-08-19 23:38:37 +00:00
|
|
|
switch (index) {
|
2012-05-01 19:16:32 +00:00
|
|
|
#if AVIAN_TARGET_PLATFORM == AVIAN_PLATFORM_WINDOWS
|
2009-06-11 15:40:50 +00:00
|
|
|
case 0:
|
|
|
|
return rcx;
|
|
|
|
case 1:
|
|
|
|
return rdx;
|
|
|
|
case 2:
|
|
|
|
return r8;
|
|
|
|
case 3:
|
|
|
|
return r9;
|
2009-07-27 13:49:54 +00:00
|
|
|
#else
|
2008-08-19 23:38:37 +00:00
|
|
|
case 0:
|
|
|
|
return rdi;
|
|
|
|
case 1:
|
|
|
|
return rsi;
|
|
|
|
case 2:
|
|
|
|
return rdx;
|
|
|
|
case 3:
|
|
|
|
return rcx;
|
|
|
|
case 4:
|
|
|
|
return r8;
|
|
|
|
case 5:
|
|
|
|
return r9;
|
2009-06-11 15:40:50 +00:00
|
|
|
#endif
|
2008-08-19 23:38:37 +00:00
|
|
|
default:
|
|
|
|
abort(&c);
|
|
|
|
}
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2011-02-21 22:25:52 +00:00
|
|
|
virtual bool hasLinkRegister() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-04-25 17:49:56 +00:00
|
|
|
virtual unsigned stackAlignmentInWords() {
|
|
|
|
return StackAlignmentInWords;
|
|
|
|
}
|
|
|
|
|
2009-04-07 00:34:12 +00:00
|
|
|
virtual bool matchCall(void* returnAddress, void* target) {
|
|
|
|
uint8_t* instruction = static_cast<uint8_t*>(returnAddress) - 5;
|
|
|
|
int32_t actualOffset; memcpy(&actualOffset, instruction + 1, 4);
|
|
|
|
void* actualTarget = static_cast<uint8_t*>(returnAddress) + actualOffset;
|
|
|
|
|
|
|
|
return *instruction == 0xE8 and actualTarget == target;
|
|
|
|
}
|
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
virtual void updateCall(UnaryOperation op, void* returnAddress,
|
|
|
|
void* newTarget)
|
2008-02-08 23:18:57 +00:00
|
|
|
{
|
2011-03-26 20:43:03 +00:00
|
|
|
bool assertAlignment UNUSED;
|
2009-10-18 00:18:03 +00:00
|
|
|
switch (op) {
|
|
|
|
case AlignedCall:
|
|
|
|
op = Call;
|
|
|
|
assertAlignment = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AlignedJump:
|
|
|
|
op = Jump;
|
|
|
|
assertAlignment = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AlignedLongCall:
|
|
|
|
op = LongCall;
|
|
|
|
assertAlignment = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AlignedLongJump:
|
|
|
|
op = LongJump;
|
|
|
|
assertAlignment = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assertAlignment = false;
|
|
|
|
}
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 or op == Call or op == Jump) {
|
2009-02-09 23:22:01 +00:00
|
|
|
uint8_t* instruction = static_cast<uint8_t*>(returnAddress) - 5;
|
2009-06-11 15:40:50 +00:00
|
|
|
|
2009-02-09 23:22:01 +00:00
|
|
|
assert(&c, ((op == Call or op == LongCall) and *instruction == 0xE8)
|
|
|
|
or ((op == Jump or op == LongJump) and *instruction == 0xE9));
|
|
|
|
|
|
|
|
assert(&c, (not assertAlignment)
|
|
|
|
or reinterpret_cast<uintptr_t>(instruction + 1) % 4 == 0);
|
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
intptr_t v = static_cast<uint8_t*>(newTarget)
|
2009-02-09 23:22:01 +00:00
|
|
|
- static_cast<uint8_t*>(returnAddress);
|
2009-10-18 00:18:03 +00:00
|
|
|
|
|
|
|
assert(&c, isInt32(v));
|
|
|
|
|
|
|
|
int32_t v32 = v;
|
|
|
|
|
|
|
|
memcpy(instruction + 1, &v32, 4);
|
2008-05-04 19:09:12 +00:00
|
|
|
} else {
|
2009-02-09 23:22:01 +00:00
|
|
|
uint8_t* instruction = static_cast<uint8_t*>(returnAddress) - 13;
|
|
|
|
|
|
|
|
assert(&c, instruction[0] == 0x49 and instruction[1] == 0xBA);
|
|
|
|
assert(&c, instruction[10] == 0x41 and instruction[11] == 0xFF);
|
|
|
|
assert(&c, (op == LongCall and instruction[12] == 0xD2)
|
|
|
|
or (op == LongJump and instruction[12] == 0xE2));
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2009-02-09 23:22:01 +00:00
|
|
|
assert(&c, (not assertAlignment)
|
|
|
|
or reinterpret_cast<uintptr_t>(instruction + 2) % 8 == 0);
|
|
|
|
|
|
|
|
memcpy(instruction + 2, &newTarget, 8);
|
2008-05-04 19:09:12 +00:00
|
|
|
}
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
virtual void setConstant(void* dst, uint64_t constant) {
|
2011-09-17 02:53:08 +00:00
|
|
|
target_uintptr_t v = targetVW(constant);
|
2011-08-30 01:00:17 +00:00
|
|
|
memcpy(dst, &v, TargetBytesPerWord);
|
2009-03-10 00:52:09 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned alignFrameSize(unsigned sizeInWords) {
|
2009-05-17 00:39:08 +00:00
|
|
|
return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords)
|
2008-09-07 20:12:11 +00:00
|
|
|
- FrameHeaderSize;
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2011-01-26 00:22:43 +00:00
|
|
|
virtual void nextFrame(void* start, unsigned size, unsigned footprint,
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
void* link, bool mostRecent,
|
2011-01-26 00:22:43 +00:00
|
|
|
unsigned targetParameterFootprint, void** ip,
|
|
|
|
void** stack)
|
|
|
|
{
|
|
|
|
local::nextFrame(&c, static_cast<uint8_t*>(start), size, footprint,
|
fix a couple of subtle Thread.getStackTrace bugs
The first problem was that, on x86, we failed to properly keep track
of whether to expect the return address to be on the stack or not when
unwinding through a frame. We were relying on a "stackLimit" pointer
to tell us whether we were looking at the most recently-called frame
by comparing it with the stack pointer for that frame. That was
inaccurate in the case of a thread executing at the beginning of a
method before a new frame is allocated, in which case the most recent
two frames share a stack pointer, confusing the unwinder. The
solution involves keeping track of how many frames we've looked at
while walking the stack.
The other problem was that compareIpToMethodBounds assumed every
method was followed by at least one byte of padding before the next
method started. That assumption was usually valid because we were
storing the size following method code prior to the code itself.
However, the last method of an AOT-compiled code image is not followed
by any such method header and may instead be followed directly by
native code with no intervening padding. In that case, we risk
interpreting that native code as part of the preceding method, with
potentially bizarre results.
The reason for the compareIpToMethodBounds assumption was that methods
which throw exceptions as their last instruction generate a
non-returning call, which nonetheless push a return address on the
stack which points past the end of the method, and the unwinder needs
to know that return address belongs to that method. A better solution
is to add an extra trap instruction to the end of such methods, which
is what this patch does.
2012-05-05 00:35:13 +00:00
|
|
|
link, mostRecent, targetParameterFootprint, ip, stack);
|
2011-01-26 00:22:43 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void* frameIp(void* stack) {
|
2008-10-05 00:12:29 +00:00
|
|
|
return stack ? *static_cast<void**>(stack) : 0;
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned frameHeaderSize() {
|
2008-09-07 20:12:11 +00:00
|
|
|
return FrameHeaderSize;
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-11-08 22:36:38 +00:00
|
|
|
virtual unsigned frameReturnAddressSize() {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned frameFooterSize() {
|
|
|
|
return 0;
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
virtual bool alwaysCondensed(BinaryOperation op) {
|
2009-08-10 19:20:23 +00:00
|
|
|
switch(op) {
|
|
|
|
case Float2Float:
|
|
|
|
case Float2Int:
|
|
|
|
case Int2Float:
|
2009-10-18 01:26:14 +00:00
|
|
|
case FloatAbsolute:
|
2009-08-10 19:20:23 +00:00
|
|
|
case FloatNegate:
|
2009-10-18 01:26:14 +00:00
|
|
|
case FloatSquareRoot:
|
2009-08-11 19:30:31 +00:00
|
|
|
return false;
|
2009-10-10 21:03:23 +00:00
|
|
|
|
2009-08-11 19:30:31 +00:00
|
|
|
case Negate:
|
2009-10-18 01:26:14 +00:00
|
|
|
case Absolute:
|
2009-08-10 19:20:23 +00:00
|
|
|
return true;
|
2009-10-10 21:03:23 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
abort(&c);
|
2009-08-10 19:20:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool alwaysCondensed(TernaryOperation) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-04-22 01:39:25 +00:00
|
|
|
virtual int returnAddressOffset() {
|
|
|
|
return 0;
|
2009-04-19 22:36:11 +00:00
|
|
|
}
|
|
|
|
|
2009-04-22 01:39:25 +00:00
|
|
|
virtual int framePointerOffset() {
|
2011-01-27 18:54:41 +00:00
|
|
|
return UseFramePointer ? -1 : 0;
|
2009-04-19 22:36:11 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void plan
|
|
|
|
(UnaryOperation,
|
|
|
|
unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask,
|
|
|
|
bool* thunk)
|
|
|
|
{
|
2009-02-26 01:13:18 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand)
|
|
|
|
| (1 << ConstantOperand);
|
2008-08-19 23:38:37 +00:00
|
|
|
*aRegisterMask = ~static_cast<uint64_t>(0);
|
|
|
|
*thunk = false;
|
|
|
|
}
|
2008-03-13 23:43:11 +00:00
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
virtual void planSource
|
2008-08-19 23:38:37 +00:00
|
|
|
(BinaryOperation op,
|
|
|
|
unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask,
|
2009-08-06 16:26:22 +00:00
|
|
|
unsigned bSize, bool* thunk)
|
2008-08-19 23:38:37 +00:00
|
|
|
{
|
|
|
|
*aTypeMask = ~0;
|
2009-09-20 21:43:32 +00:00
|
|
|
*aRegisterMask = GeneralRegisterMask |
|
|
|
|
(static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
2008-03-13 23:43:11 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
*thunk = false;
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
switch (op) {
|
2008-11-11 00:07:44 +00:00
|
|
|
case Negate:
|
|
|
|
*aTypeMask = (1 << RegisterOperand);
|
2008-11-11 03:23:33 +00:00
|
|
|
*aRegisterMask = (static_cast<uint64_t>(1) << (rdx + 32))
|
|
|
|
| (static_cast<uint64_t>(1) << rax);
|
2009-08-06 16:26:22 +00:00
|
|
|
break;
|
2009-09-20 21:43:32 +00:00
|
|
|
|
2009-10-18 01:26:14 +00:00
|
|
|
case Absolute:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (aSize <= TargetBytesPerWord) {
|
2010-11-30 18:36:18 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand);
|
|
|
|
*aRegisterMask = (static_cast<uint64_t>(1) << rax);
|
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
2009-08-06 16:26:22 +00:00
|
|
|
break;
|
2009-09-20 21:43:32 +00:00
|
|
|
|
2009-10-18 01:26:14 +00:00
|
|
|
case FloatAbsolute:
|
2009-12-01 05:02:26 +00:00
|
|
|
if (useSSE(&c)) {
|
|
|
|
*aTypeMask = (1 << RegisterOperand);
|
|
|
|
*aRegisterMask = (static_cast<uint64_t>(FloatRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
2009-09-20 21:43:32 +00:00
|
|
|
break;
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
case FloatNegate:
|
2009-09-20 21:43:32 +00:00
|
|
|
// floatNegateRR does not support doubles
|
2009-10-10 23:46:43 +00:00
|
|
|
if (useSSE(&c) and aSize == 4 and bSize == 4) {
|
2009-09-20 21:43:32 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand);
|
2009-08-06 16:26:22 +00:00
|
|
|
*aRegisterMask = FloatRegisterMask;
|
2009-09-20 21:43:32 +00:00
|
|
|
} else {
|
|
|
|
*thunk = true;
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-09-20 21:43:32 +00:00
|
|
|
|
2009-10-18 01:26:14 +00:00
|
|
|
case FloatSquareRoot:
|
2009-12-01 05:02:26 +00:00
|
|
|
if (useSSE(&c)) {
|
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
*aRegisterMask = (static_cast<uint64_t>(FloatRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
2009-09-20 21:43:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Float2Float:
|
2009-10-10 23:46:43 +00:00
|
|
|
if (useSSE(&c)) {
|
2009-08-06 16:26:22 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
2009-10-04 19:56:48 +00:00
|
|
|
*aRegisterMask = (static_cast<uint64_t>(FloatRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
2009-09-20 21:43:32 +00:00
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Float2Int:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (useSSE(&c) and bSize <= TargetBytesPerWord) {
|
2009-10-05 14:25:12 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
2009-10-04 19:56:48 +00:00
|
|
|
*aRegisterMask = (static_cast<uint64_t>(FloatRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
2009-09-20 21:43:32 +00:00
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Int2Float:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (useSSE(&c) and aSize <= TargetBytesPerWord) {
|
2009-10-05 14:25:12 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
2009-09-20 21:43:32 +00:00
|
|
|
*aRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
case Move:
|
2009-11-30 15:09:43 +00:00
|
|
|
*aTypeMask = ~0;
|
2009-11-30 02:17:08 +00:00
|
|
|
*aRegisterMask = ~static_cast<uint64_t>(0);
|
2009-09-20 21:43:32 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4) {
|
2009-08-06 16:26:22 +00:00
|
|
|
if (aSize == 4 and bSize == 8) {
|
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
2009-09-20 21:43:32 +00:00
|
|
|
const uint32_t mask
|
|
|
|
= GeneralRegisterMask & ~((1 << rax) | (1 << rdx));
|
2009-08-06 16:26:22 +00:00
|
|
|
*aRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
|
|
|
} else if (aSize == 1 or bSize == 1) {
|
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
const uint32_t mask
|
|
|
|
= (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx);
|
|
|
|
*aRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2009-09-20 21:43:32 +00:00
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void planDestination
|
2009-10-04 19:56:48 +00:00
|
|
|
(BinaryOperation op, unsigned aSize, uint8_t aTypeMask,
|
|
|
|
uint64_t aRegisterMask, unsigned bSize, uint8_t* bTypeMask,
|
2009-09-20 21:43:32 +00:00
|
|
|
uint64_t* bRegisterMask)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-09-20 21:43:32 +00:00
|
|
|
*bTypeMask = ~0;
|
|
|
|
*bRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
switch (op) {
|
2009-10-18 01:26:14 +00:00
|
|
|
case Absolute:
|
2009-08-06 16:26:22 +00:00
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
|
|
|
*bRegisterMask = (static_cast<uint64_t>(1) << rax);
|
2009-09-20 21:43:32 +00:00
|
|
|
break;
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2009-10-18 01:26:14 +00:00
|
|
|
case FloatAbsolute:
|
2009-08-06 16:26:22 +00:00
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
2009-10-04 19:56:48 +00:00
|
|
|
*bRegisterMask = aRegisterMask;
|
2008-11-11 00:07:44 +00:00
|
|
|
break;
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
case Negate:
|
2009-09-20 21:43:32 +00:00
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
2009-10-04 19:56:48 +00:00
|
|
|
*bRegisterMask = aRegisterMask;
|
2009-09-20 21:43:32 +00:00
|
|
|
break;
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
case FloatNegate:
|
2009-10-18 01:26:14 +00:00
|
|
|
case FloatSquareRoot:
|
2009-09-20 21:43:32 +00:00
|
|
|
case Float2Float:
|
|
|
|
case Int2Float:
|
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
2009-10-04 19:56:48 +00:00
|
|
|
*bRegisterMask = (static_cast<uint64_t>(FloatRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
2009-09-20 21:43:32 +00:00
|
|
|
break;
|
2009-10-04 19:56:48 +00:00
|
|
|
|
2009-09-20 21:43:32 +00:00
|
|
|
case Float2Int:
|
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
|
|
|
break;
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
case Move:
|
2009-10-04 19:56:48 +00:00
|
|
|
if (aTypeMask & ((1 << MemoryOperand) | 1 << AddressOperand)) {
|
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
|
|
|
*bRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
|
|
|
} else if (aTypeMask & (1 << RegisterOperand)) {
|
|
|
|
*bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
if (aRegisterMask & FloatRegisterMask) {
|
|
|
|
*bRegisterMask = FloatRegisterMask;
|
|
|
|
} else {
|
|
|
|
*bRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
}
|
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4) {
|
2008-08-19 23:38:37 +00:00
|
|
|
if (aSize == 4 and bSize == 8) {
|
|
|
|
*bRegisterMask = (static_cast<uint64_t>(1) << (rdx + 32))
|
2009-08-06 16:26:22 +00:00
|
|
|
| (static_cast<uint64_t>(1) << rax);
|
2009-03-14 22:47:26 +00:00
|
|
|
} else if (aSize == 1 or bSize == 1) {
|
2008-08-19 23:38:37 +00:00
|
|
|
const uint32_t mask
|
|
|
|
= (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx);
|
2009-08-06 16:26:22 +00:00
|
|
|
*bRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-04-29 23:16:53 +00:00
|
|
|
}
|
2008-08-19 23:38:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2008-03-13 23:43:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-04 19:56:48 +00:00
|
|
|
virtual void planMove
|
2009-11-28 04:15:12 +00:00
|
|
|
(unsigned size, uint8_t* srcTypeMask, uint64_t* srcRegisterMask,
|
|
|
|
uint8_t* tmpTypeMask, uint64_t* tmpRegisterMask,
|
|
|
|
uint8_t dstTypeMask, uint64_t dstRegisterMask)
|
2009-10-04 19:56:48 +00:00
|
|
|
{
|
2009-11-28 04:15:12 +00:00
|
|
|
*srcTypeMask = ~0;
|
|
|
|
*srcRegisterMask = ~static_cast<uint64_t>(0);
|
2009-10-04 19:56:48 +00:00
|
|
|
|
2009-11-28 04:15:12 +00:00
|
|
|
*tmpTypeMask = 0;
|
|
|
|
*tmpRegisterMask = 0;
|
|
|
|
|
|
|
|
if (dstTypeMask & (1 << MemoryOperand)) {
|
2009-10-04 19:56:48 +00:00
|
|
|
// can't move directly from memory to memory
|
2009-11-28 04:15:12 +00:00
|
|
|
*srcTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand);
|
|
|
|
*tmpTypeMask = 1 << RegisterOperand;
|
2009-10-04 19:56:48 +00:00
|
|
|
*tmpRegisterMask = GeneralRegisterMask
|
2009-10-04 22:10:36 +00:00
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
2009-10-04 19:56:48 +00:00
|
|
|
} else if (dstTypeMask & (1 << RegisterOperand)) {
|
2011-08-30 01:00:17 +00:00
|
|
|
if (size > TargetBytesPerWord) {
|
2009-11-28 04:15:12 +00:00
|
|
|
// can't move directly from FPR to GPR or vice-versa for
|
|
|
|
// values larger than the GPR size
|
|
|
|
if (dstRegisterMask & FloatRegisterMask) {
|
|
|
|
*srcRegisterMask = FloatRegisterMask
|
|
|
|
| (static_cast<uint64_t>(FloatRegisterMask) << 32);
|
|
|
|
*tmpTypeMask = 1 << MemoryOperand;
|
|
|
|
} else if (dstRegisterMask & GeneralRegisterMask) {
|
|
|
|
*srcRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
|
|
|
*tmpTypeMask = 1 << MemoryOperand;
|
2009-10-04 19:56:48 +00:00
|
|
|
}
|
2009-11-28 04:15:12 +00:00
|
|
|
}
|
|
|
|
if (dstRegisterMask & FloatRegisterMask) {
|
2009-10-04 19:56:48 +00:00
|
|
|
// can't move directly from constant to FPR
|
2009-11-28 04:15:12 +00:00
|
|
|
*srcTypeMask &= ~(1 << ConstantOperand);
|
2011-08-30 01:00:17 +00:00
|
|
|
if (size > TargetBytesPerWord) {
|
2009-11-28 04:15:12 +00:00
|
|
|
*tmpTypeMask = 1 << MemoryOperand;
|
|
|
|
} else {
|
|
|
|
*tmpTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
*tmpRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
|
|
|
}
|
2009-10-04 19:56:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
virtual void planSource
|
2008-09-09 00:31:19 +00:00
|
|
|
(TernaryOperation op,
|
2009-08-06 16:26:22 +00:00
|
|
|
unsigned aSize, uint8_t *aTypeMask, uint64_t *aRegisterMask,
|
2008-08-19 23:38:37 +00:00
|
|
|
unsigned, uint8_t* bTypeMask, uint64_t* bRegisterMask,
|
2009-08-06 16:26:22 +00:00
|
|
|
unsigned, bool* thunk)
|
2008-08-19 23:38:37 +00:00
|
|
|
{
|
2008-09-15 02:28:42 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand);
|
2009-10-04 22:10:36 +00:00
|
|
|
*aRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
2008-03-15 23:54:20 +00:00
|
|
|
|
2008-09-15 02:28:42 +00:00
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
2009-10-04 22:10:36 +00:00
|
|
|
*bRegisterMask = GeneralRegisterMask
|
|
|
|
| (static_cast<uint64_t>(GeneralRegisterMask) << 32);
|
2008-03-15 23:54:20 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
*thunk = false;
|
2008-09-09 00:31:19 +00:00
|
|
|
|
|
|
|
switch (op) {
|
2009-09-20 21:43:32 +00:00
|
|
|
case FloatAdd:
|
|
|
|
case FloatSubtract:
|
|
|
|
case FloatMultiply:
|
|
|
|
case FloatDivide:
|
2009-10-10 23:46:43 +00:00
|
|
|
if (useSSE(&c)) {
|
2009-09-20 21:43:32 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
*bTypeMask = (1 << RegisterOperand);
|
2009-09-26 19:43:44 +00:00
|
|
|
|
|
|
|
const uint64_t mask
|
|
|
|
= (static_cast<uint64_t>(FloatRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
|
|
|
*aRegisterMask = mask;
|
|
|
|
*bRegisterMask = mask;
|
2009-09-20 21:43:32 +00:00
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
|
|
|
break;
|
2009-10-25 01:29:20 +00:00
|
|
|
|
|
|
|
case FloatRemainder:
|
|
|
|
*thunk = true;
|
|
|
|
break;
|
2009-08-06 16:26:22 +00:00
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
case Multiply:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2009-08-06 16:26:22 +00:00
|
|
|
const uint32_t mask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx));
|
2008-09-09 00:31:19 +00:00
|
|
|
*aRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
|
|
|
*bRegisterMask = (static_cast<uint64_t>(1) << (rdx + 32)) | mask;
|
2009-08-06 16:26:22 +00:00
|
|
|
} else {
|
|
|
|
*aRegisterMask = GeneralRegisterMask;
|
|
|
|
*bRegisterMask = GeneralRegisterMask;
|
2008-09-09 00:31:19 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Divide:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2009-08-06 16:26:22 +00:00
|
|
|
*thunk = true;
|
2008-09-09 00:31:19 +00:00
|
|
|
} else {
|
2008-12-12 01:08:16 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand);
|
2009-08-06 16:26:22 +00:00
|
|
|
*aRegisterMask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx));
|
2008-09-09 00:31:19 +00:00
|
|
|
*bRegisterMask = 1 << rax;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Remainder:
|
2011-08-30 01:00:17 +00:00
|
|
|
if (TargetBytesPerWord == 4 and aSize == 8) {
|
2008-09-09 00:31:19 +00:00
|
|
|
*thunk = true;
|
|
|
|
} else {
|
2008-12-12 01:08:16 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand);
|
2009-08-06 16:26:22 +00:00
|
|
|
*aRegisterMask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx));
|
|
|
|
*bRegisterMask = 1 << rax;
|
2008-09-09 00:31:19 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ShiftLeft:
|
|
|
|
case ShiftRight:
|
|
|
|
case UnsignedShiftRight: {
|
2009-08-06 16:26:22 +00:00
|
|
|
*aRegisterMask = (static_cast<uint64_t>(GeneralRegisterMask) << 32)
|
2008-09-09 00:31:19 +00:00
|
|
|
| (static_cast<uint64_t>(1) << rcx);
|
2009-08-06 16:26:22 +00:00
|
|
|
const uint32_t mask = GeneralRegisterMask & ~(1 << rcx);
|
2008-09-09 00:31:19 +00:00
|
|
|
*bRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
|
|
|
} break;
|
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
case JumpIfFloatEqual:
|
|
|
|
case JumpIfFloatNotEqual:
|
|
|
|
case JumpIfFloatLess:
|
|
|
|
case JumpIfFloatGreater:
|
|
|
|
case JumpIfFloatLessOrEqual:
|
|
|
|
case JumpIfFloatGreaterOrEqual:
|
|
|
|
case JumpIfFloatLessOrUnordered:
|
|
|
|
case JumpIfFloatGreaterOrUnordered:
|
|
|
|
case JumpIfFloatLessOrEqualOrUnordered:
|
|
|
|
case JumpIfFloatGreaterOrEqualOrUnordered:
|
2009-10-10 23:46:43 +00:00
|
|
|
if (useSSE(&c)) {
|
2009-10-10 21:03:23 +00:00
|
|
|
*aTypeMask = (1 << RegisterOperand);
|
|
|
|
*aRegisterMask = (static_cast<uint64_t>(FloatRegisterMask) << 32)
|
|
|
|
| FloatRegisterMask;
|
|
|
|
*bTypeMask = *aTypeMask;
|
|
|
|
*bRegisterMask = *aRegisterMask;
|
|
|
|
} else {
|
|
|
|
*thunk = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-08-06 16:26:22 +00:00
|
|
|
}
|
2008-09-09 00:31:19 +00:00
|
|
|
|
2009-08-06 16:26:22 +00:00
|
|
|
virtual void planDestination
|
2009-10-10 21:03:23 +00:00
|
|
|
(TernaryOperation op, unsigned, uint8_t, uint64_t, unsigned, uint8_t,
|
2009-10-04 19:56:48 +00:00
|
|
|
uint64_t bRegisterMask, unsigned, uint8_t* cTypeMask,
|
|
|
|
uint64_t* cRegisterMask)
|
2009-08-06 16:26:22 +00:00
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
if (isBranch(op)) {
|
|
|
|
*cTypeMask = (1 << ConstantOperand);
|
|
|
|
*cRegisterMask = 0;
|
|
|
|
} else {
|
|
|
|
*cTypeMask = (1 << RegisterOperand);
|
|
|
|
*cRegisterMask = bRegisterMask;
|
|
|
|
}
|
2008-02-12 15:21:51 +00:00
|
|
|
}
|
2008-03-10 22:37:21 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void acquire() {
|
|
|
|
++ referenceCount;
|
2008-03-10 22:37:21 +00:00
|
|
|
}
|
2008-02-12 15:21:51 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void release() {
|
|
|
|
if (-- referenceCount == 0) {
|
|
|
|
c.s->free(this);
|
2008-02-17 22:29:04 +00:00
|
|
|
}
|
2008-02-12 15:21:51 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
ArchitectureContext c;
|
|
|
|
unsigned referenceCount;
|
|
|
|
};
|
2008-03-09 21:27:51 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
class MyAssembler: public Assembler {
|
|
|
|
public:
|
2008-08-23 18:04:36 +00:00
|
|
|
MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch):
|
2009-11-30 15:38:16 +00:00
|
|
|
c(s, a, zone, &(arch->c)), arch_(arch)
|
2008-08-19 23:38:37 +00:00
|
|
|
{ }
|
2008-03-09 21:27:51 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void setClient(Client* client) {
|
|
|
|
assert(&c, c.client == 0);
|
|
|
|
c.client = client;
|
2008-03-09 21:27:51 +00:00
|
|
|
}
|
|
|
|
|
2008-08-23 18:04:36 +00:00
|
|
|
virtual Architecture* arch() {
|
|
|
|
return arch_;
|
|
|
|
}
|
|
|
|
|
2011-01-30 21:14:57 +00:00
|
|
|
virtual void checkStackOverflow(uintptr_t handler,
|
|
|
|
unsigned stackLimitOffsetFromThread)
|
|
|
|
{
|
|
|
|
Register stack(rsp);
|
|
|
|
Memory stackLimit(rbx, stackLimitOffsetFromThread);
|
|
|
|
Constant handlerConstant(resolved(&c, handler));
|
2011-08-30 01:00:17 +00:00
|
|
|
branchRM(&c, JumpIfGreaterOrEqual, TargetBytesPerWord, &stack, &stackLimit,
|
2011-01-30 21:14:57 +00:00
|
|
|
&handlerConstant);
|
|
|
|
}
|
|
|
|
|
2011-02-20 03:33:26 +00:00
|
|
|
virtual void saveFrame(unsigned stackOffset, unsigned) {
|
2008-09-05 15:00:38 +00:00
|
|
|
Register stack(rsp);
|
|
|
|
Memory stackDst(rbx, stackOffset);
|
2011-08-30 01:00:17 +00:00
|
|
|
apply(Move, TargetBytesPerWord, RegisterOperand, &stack,
|
|
|
|
TargetBytesPerWord, MemoryOperand, &stackDst);
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void pushFrame(unsigned argumentCount, ...) {
|
2009-08-27 00:26:44 +00:00
|
|
|
struct Argument {
|
2008-09-05 15:00:38 +00:00
|
|
|
unsigned size;
|
|
|
|
OperandType type;
|
|
|
|
Operand* operand;
|
2009-08-27 00:26:44 +00:00
|
|
|
};
|
|
|
|
RUNTIME_ARRAY(Argument, arguments, argumentCount);
|
2008-09-05 15:00:38 +00:00
|
|
|
va_list a; va_start(a, argumentCount);
|
|
|
|
unsigned footprint = 0;
|
|
|
|
for (unsigned i = 0; i < argumentCount; ++i) {
|
2009-08-27 00:26:44 +00:00
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].size = va_arg(a, unsigned);
|
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].type
|
|
|
|
= static_cast<OperandType>(va_arg(a, int));
|
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].operand = va_arg(a, Operand*);
|
|
|
|
footprint += ceiling
|
2011-08-30 01:00:17 +00:00
|
|
|
(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord);
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
va_end(a);
|
|
|
|
|
2008-09-07 20:12:11 +00:00
|
|
|
allocateFrame(arch_->alignFrameSize(footprint));
|
2008-09-05 15:00:38 +00:00
|
|
|
|
2008-09-07 20:12:11 +00:00
|
|
|
unsigned offset = 0;
|
2008-09-05 15:00:38 +00:00
|
|
|
for (unsigned i = 0; i < argumentCount; ++i) {
|
2008-09-23 21:18:41 +00:00
|
|
|
if (i < arch_->argumentRegisterCount()) {
|
|
|
|
Register dst(arch_->argumentRegister(i));
|
|
|
|
apply(Move,
|
2009-08-27 00:26:44 +00:00
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].size,
|
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].type,
|
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].operand,
|
2011-09-01 03:18:00 +00:00
|
|
|
pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord),
|
2009-08-27 00:26:44 +00:00
|
|
|
RegisterOperand,
|
|
|
|
&dst);
|
2008-09-23 21:18:41 +00:00
|
|
|
} else {
|
2011-08-30 01:00:17 +00:00
|
|
|
Memory dst(rsp, offset * TargetBytesPerWord);
|
2008-09-23 21:18:41 +00:00
|
|
|
apply(Move,
|
2009-08-27 00:26:44 +00:00
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].size,
|
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].type,
|
|
|
|
RUNTIME_ARRAY_BODY(arguments)[i].operand,
|
2011-09-01 03:18:00 +00:00
|
|
|
pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord),
|
2009-08-27 00:26:44 +00:00
|
|
|
MemoryOperand,
|
|
|
|
&dst);
|
2011-08-30 01:00:17 +00:00
|
|
|
offset += ceiling
|
|
|
|
(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord);
|
2008-09-23 21:18:41 +00:00
|
|
|
}
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void allocateFrame(unsigned footprint) {
|
|
|
|
Register stack(rsp);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
|
|
|
|
if (UseFramePointer) {
|
|
|
|
Register base(rbp);
|
2011-08-30 01:00:17 +00:00
|
|
|
pushR(&c, TargetBytesPerWord, &base);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
apply(Move, TargetBytesPerWord, RegisterOperand, &stack,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &base);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
}
|
2008-09-05 15:00:38 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
Constant footprintConstant(resolved(&c, footprint * TargetBytesPerWord));
|
|
|
|
apply(Subtract, TargetBytesPerWord, ConstantOperand, &footprintConstant,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &stack,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &stack);
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
virtual void adjustFrame(unsigned difference) {
|
2009-05-27 01:02:39 +00:00
|
|
|
Register stack(rsp);
|
2011-08-30 01:00:17 +00:00
|
|
|
Constant differenceConstant(resolved(&c, difference * TargetBytesPerWord));
|
|
|
|
apply(Subtract, TargetBytesPerWord, ConstantOperand, &differenceConstant,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &stack,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &stack);
|
2009-05-27 01:02:39 +00:00
|
|
|
}
|
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
virtual void popFrame(unsigned frameFootprint) {
|
|
|
|
if (UseFramePointer) {
|
|
|
|
Register base(rbp);
|
|
|
|
Register stack(rsp);
|
2011-08-30 01:00:17 +00:00
|
|
|
apply(Move, TargetBytesPerWord, RegisterOperand, &base,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &stack);
|
2008-09-05 15:00:38 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
popR(&c, TargetBytesPerWord, &base);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
} else {
|
|
|
|
Register stack(rsp);
|
2011-08-30 01:00:17 +00:00
|
|
|
Constant footprint(resolved(&c, frameFootprint * TargetBytesPerWord));
|
|
|
|
apply(Add, TargetBytesPerWord, ConstantOperand, &footprint,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &stack,
|
|
|
|
TargetBytesPerWord, RegisterOperand, &stack);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
}
|
2008-09-05 15:00:38 +00:00
|
|
|
}
|
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
virtual void popFrameForTailCall(unsigned frameFootprint,
|
2009-04-19 22:36:11 +00:00
|
|
|
int offset,
|
|
|
|
int returnAddressSurrogate,
|
|
|
|
int framePointerSurrogate)
|
|
|
|
{
|
2009-05-26 05:27:10 +00:00
|
|
|
if (TailCalls) {
|
|
|
|
if (offset) {
|
|
|
|
Register tmp(c.client->acquireTemporary());
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2011-01-26 00:22:43 +00:00
|
|
|
unsigned baseSize = UseFramePointer ? 1 : 0;
|
|
|
|
|
|
|
|
Memory returnAddressSrc
|
2011-08-30 01:00:17 +00:00
|
|
|
(rsp, (frameFootprint + baseSize) * TargetBytesPerWord);
|
|
|
|
moveMR(&c, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord,
|
|
|
|
&tmp);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
Memory returnAddressDst
|
2011-08-30 01:00:17 +00:00
|
|
|
(rsp, (frameFootprint - offset + baseSize) * TargetBytesPerWord);
|
|
|
|
moveRM(&c, TargetBytesPerWord, &tmp, TargetBytesPerWord,
|
|
|
|
&returnAddressDst);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2009-05-26 05:27:10 +00:00
|
|
|
c.client->releaseTemporary(tmp.low);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
if (UseFramePointer) {
|
2011-08-30 01:00:17 +00:00
|
|
|
Memory baseSrc(rsp, frameFootprint * TargetBytesPerWord);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
Register base(rbp);
|
2011-08-30 01:00:17 +00:00
|
|
|
moveMR(&c, TargetBytesPerWord, &baseSrc, TargetBytesPerWord, &base);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
}
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2009-05-26 05:27:10 +00:00
|
|
|
Register stack(rsp);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
Constant footprint
|
2011-08-30 01:00:17 +00:00
|
|
|
(resolved
|
|
|
|
(&c, (frameFootprint - offset + baseSize) * TargetBytesPerWord));
|
|
|
|
|
|
|
|
addCR(&c, TargetBytesPerWord, &footprint, TargetBytesPerWord, &stack);
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
|
2009-05-26 05:27:10 +00:00
|
|
|
if (returnAddressSurrogate != NoRegister) {
|
|
|
|
assert(&c, offset > 0);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2009-05-26 05:27:10 +00:00
|
|
|
Register ras(returnAddressSurrogate);
|
2011-08-30 01:00:17 +00:00
|
|
|
Memory dst(rsp, offset * TargetBytesPerWord);
|
|
|
|
moveRM(&c, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst);
|
2009-05-26 05:27:10 +00:00
|
|
|
}
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2009-05-26 05:27:10 +00:00
|
|
|
if (framePointerSurrogate != NoRegister) {
|
|
|
|
assert(&c, offset > 0);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2009-05-26 05:27:10 +00:00
|
|
|
Register fps(framePointerSurrogate);
|
2011-08-30 01:00:17 +00:00
|
|
|
Memory dst(rsp, (offset - 1) * TargetBytesPerWord);
|
|
|
|
moveRM(&c, TargetBytesPerWord, &fps, TargetBytesPerWord, &dst);
|
2009-05-26 05:27:10 +00:00
|
|
|
}
|
|
|
|
} else {
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
popFrame(frameFootprint);
|
2009-04-19 22:36:11 +00:00
|
|
|
}
|
|
|
|
} else {
|
2009-05-26 05:27:10 +00:00
|
|
|
abort(&c);
|
|
|
|
}
|
2009-04-19 22:36:11 +00:00
|
|
|
}
|
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint,
|
|
|
|
unsigned argumentFootprint)
|
|
|
|
{
|
|
|
|
popFrame(frameFootprint);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
|
|
|
assert(&c, argumentFootprint >= StackAlignmentInWords);
|
|
|
|
assert(&c, (argumentFootprint % StackAlignmentInWords) == 0);
|
|
|
|
|
2009-05-26 05:27:10 +00:00
|
|
|
if (TailCalls and argumentFootprint > StackAlignmentInWords) {
|
2009-04-19 22:36:11 +00:00
|
|
|
Register returnAddress(rcx);
|
2011-08-30 01:00:17 +00:00
|
|
|
popR(&c, TargetBytesPerWord, &returnAddress);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
|
|
|
Register stack(rsp);
|
|
|
|
Constant adjustment
|
|
|
|
(resolved(&c, (argumentFootprint - StackAlignmentInWords)
|
2011-08-30 01:00:17 +00:00
|
|
|
* TargetBytesPerWord));
|
|
|
|
addCR(&c, TargetBytesPerWord, &adjustment, TargetBytesPerWord, &stack);
|
2009-04-19 22:36:11 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
jumpR(&c, TargetBytesPerWord, &returnAddress);
|
2009-04-19 22:36:11 +00:00
|
|
|
} else {
|
|
|
|
return_(&c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint,
|
|
|
|
unsigned stackOffsetFromThread)
|
2009-04-25 17:49:56 +00:00
|
|
|
{
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
popFrame(frameFootprint);
|
2009-04-25 17:49:56 +00:00
|
|
|
|
|
|
|
Register returnAddress(rcx);
|
2011-08-30 01:00:17 +00:00
|
|
|
popR(&c, TargetBytesPerWord, &returnAddress);
|
2009-04-25 17:49:56 +00:00
|
|
|
|
|
|
|
Register stack(rsp);
|
|
|
|
Memory stackSrc(rbx, stackOffsetFromThread);
|
2011-08-30 01:00:17 +00:00
|
|
|
moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &stack);
|
2009-04-25 17:49:56 +00:00
|
|
|
|
2011-08-30 01:00:17 +00:00
|
|
|
jumpR(&c, TargetBytesPerWord, &returnAddress);
|
2009-04-25 17:49:56 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(Operation op) {
|
2008-08-23 18:04:36 +00:00
|
|
|
arch_->c.operations[op](&c);
|
2008-04-28 22:08:31 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(UnaryOperation op,
|
|
|
|
unsigned aSize, OperandType aType, Operand* aOperand)
|
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
arch_->c.unaryOperations[index(&(arch_->c), op, aType)]
|
|
|
|
(&c, aSize, aOperand);
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(BinaryOperation op,
|
|
|
|
unsigned aSize, OperandType aType, Operand* aOperand,
|
|
|
|
unsigned bSize, OperandType bType, Operand* bOperand)
|
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
arch_->c.binaryOperations[index(&(arch_->c), op, aType, bType)]
|
2008-08-19 23:38:37 +00:00
|
|
|
(&c, aSize, aOperand, bSize, bOperand);
|
|
|
|
}
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(TernaryOperation op,
|
|
|
|
unsigned aSize, OperandType aType, Operand* aOperand,
|
|
|
|
unsigned bSize, OperandType bType, Operand* bOperand,
|
2009-10-10 23:46:43 +00:00
|
|
|
unsigned cSize UNUSED, OperandType cType UNUSED,
|
|
|
|
Operand* cOperand)
|
2008-08-19 23:38:37 +00:00
|
|
|
{
|
2009-10-10 21:03:23 +00:00
|
|
|
if (isBranch(op)) {
|
|
|
|
assert(&c, aSize == bSize);
|
2011-08-30 01:00:17 +00:00
|
|
|
assert(&c, cSize == TargetBytesPerWord);
|
2009-10-10 21:03:23 +00:00
|
|
|
assert(&c, cType == ConstantOperand);
|
2008-09-06 21:25:41 +00:00
|
|
|
|
2009-10-10 21:03:23 +00:00
|
|
|
arch_->c.branchOperations[branchIndex(&(arch_->c), aType, bType)]
|
|
|
|
(&c, op, aSize, aOperand, bOperand, cOperand);
|
|
|
|
} else {
|
|
|
|
assert(&c, bSize == cSize);
|
|
|
|
assert(&c, bType == cType);
|
|
|
|
|
|
|
|
arch_->c.binaryOperations[index(&(arch_->c), op, aType, bType)]
|
|
|
|
(&c, aSize, aOperand, bSize, bOperand);
|
|
|
|
}
|
2008-04-28 22:08:31 +00:00
|
|
|
}
|
2008-04-26 20:56:03 +00:00
|
|
|
|
2011-02-28 06:03:13 +00:00
|
|
|
virtual void setDestination(uint8_t* dst) {
|
2008-08-19 23:38:37 +00:00
|
|
|
c.result = dst;
|
2011-02-28 06:03:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void write() {
|
|
|
|
uint8_t* dst = c.result;
|
2008-09-07 20:12:11 +00:00
|
|
|
for (MyBlock* b = c.firstBlock; b; b = b->next) {
|
2008-09-13 21:07:39 +00:00
|
|
|
unsigned index = 0;
|
2008-09-15 02:28:42 +00:00
|
|
|
unsigned padding = 0;
|
2008-09-13 21:07:39 +00:00
|
|
|
for (AlignmentPadding* p = b->firstPadding; p; p = p->next) {
|
2009-02-15 00:52:44 +00:00
|
|
|
unsigned size = p->offset - b->offset - index;
|
|
|
|
|
2008-09-15 02:28:42 +00:00
|
|
|
memcpy(dst + b->start + index + padding,
|
|
|
|
c.code.data + b->offset + index,
|
|
|
|
size);
|
2009-02-15 00:52:44 +00:00
|
|
|
|
2008-09-13 21:07:39 +00:00
|
|
|
index += size;
|
2009-02-15 00:52:44 +00:00
|
|
|
|
2009-10-18 00:18:03 +00:00
|
|
|
while ((b->start + index + padding + p->instructionOffset)
|
|
|
|
% p->alignment)
|
|
|
|
{
|
2008-09-15 02:28:42 +00:00
|
|
|
*(dst + b->start + index + padding) = 0x90;
|
|
|
|
++ padding;
|
2008-09-13 21:07:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-15 02:28:42 +00:00
|
|
|
memcpy(dst + b->start + index + padding,
|
|
|
|
c.code.data + b->offset + index,
|
2008-09-13 21:07:39 +00:00
|
|
|
b->size - index);
|
2008-09-07 20:12:11 +00:00
|
|
|
}
|
2008-08-19 23:38:37 +00:00
|
|
|
|
|
|
|
for (Task* t = c.tasks; t; t = t->next) {
|
|
|
|
t->run(&c);
|
|
|
|
}
|
|
|
|
}
|
2008-02-17 20:57:40 +00:00
|
|
|
|
2010-12-07 22:57:11 +00:00
|
|
|
virtual Promise* offset(bool) {
|
2009-08-27 00:26:44 +00:00
|
|
|
return local::offset(&c);
|
2008-08-30 20:12:27 +00:00
|
|
|
}
|
|
|
|
|
2008-09-09 00:31:19 +00:00
|
|
|
virtual Block* endBlock(bool startNew) {
|
2008-09-07 20:12:11 +00:00
|
|
|
MyBlock* b = c.lastBlock;
|
|
|
|
b->size = c.code.length() - b->offset;
|
2008-09-09 00:31:19 +00:00
|
|
|
if (startNew) {
|
2012-05-08 22:13:17 +00:00
|
|
|
c.lastBlock = new(c.zone) MyBlock(c.code.length());
|
2008-09-09 00:31:19 +00:00
|
|
|
} else {
|
|
|
|
c.lastBlock = 0;
|
|
|
|
}
|
2008-09-07 20:12:11 +00:00
|
|
|
return b;
|
2008-08-30 20:12:27 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 02:28:05 +00:00
|
|
|
virtual void endEvent() {
|
|
|
|
// ignore
|
2008-02-17 20:57:40 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 02:28:05 +00:00
|
|
|
virtual unsigned length() {
|
|
|
|
return c.code.length();
|
2009-10-20 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2011-01-30 21:14:57 +00:00
|
|
|
virtual unsigned footerSize() {
|
|
|
|
return 0;
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-17 02:05:05 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void dispose() {
|
|
|
|
c.code.dispose();
|
|
|
|
}
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
Context c;
|
2008-08-23 18:04:36 +00:00
|
|
|
MyArchitecture* arch_;
|
2008-08-19 23:38:37 +00:00
|
|
|
};
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2009-08-27 00:26:44 +00:00
|
|
|
} // namespace local
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
} // namespace
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
namespace vm {
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
Assembler::Architecture*
|
2009-10-10 23:46:43 +00:00
|
|
|
makeArchitecture(System* system, bool useNativeFeatures)
|
2008-08-19 23:38:37 +00:00
|
|
|
{
|
2009-08-27 00:26:44 +00:00
|
|
|
return new (allocate(system, sizeof(local::MyArchitecture)))
|
2009-10-10 23:46:43 +00:00
|
|
|
local::MyArchitecture(system, useNativeFeatures);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
Assembler*
|
|
|
|
makeAssembler(System* system, Allocator* allocator, Zone* zone,
|
|
|
|
Assembler::Architecture* architecture)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2012-05-08 22:13:17 +00:00
|
|
|
return
|
|
|
|
new(zone) local::MyAssembler(system, allocator, zone,
|
|
|
|
static_cast<local::MyArchitecture*>(architecture));
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-05-23 00:16:44 +00:00
|
|
|
|
2008-02-08 23:18:57 +00:00
|
|
|
} // namespace vm
|