Fixing const propagated DID_CARRY.
This commit is contained in:
parent
d65b5801f8
commit
0b42c72ec5
|
@ -2329,24 +2329,28 @@ EMITTER_ASSOCIATIVE_COMPARE_FLT_XX(UGE, setae);
|
||||||
// https://code.google.com/p/corkami/wiki/x86oddities
|
// https://code.google.com/p/corkami/wiki/x86oddities
|
||||||
EMITTER(DID_CARRY_I8, MATCH(I<OPCODE_DID_CARRY, I8<>, I8<>>)) {
|
EMITTER(DID_CARRY_I8, MATCH(I<OPCODE_DID_CARRY, I8<>, I8<>>)) {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
XEASSERT(!i.src1.is_constant);
|
||||||
e.LoadEflags();
|
e.LoadEflags();
|
||||||
e.setc(i.dest);
|
e.setc(i.dest);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
EMITTER(DID_CARRY_I16, MATCH(I<OPCODE_DID_CARRY, I8<>, I16<>>)) {
|
EMITTER(DID_CARRY_I16, MATCH(I<OPCODE_DID_CARRY, I8<>, I16<>>)) {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
XEASSERT(!i.src1.is_constant);
|
||||||
e.LoadEflags();
|
e.LoadEflags();
|
||||||
e.setc(i.dest);
|
e.setc(i.dest);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
EMITTER(DID_CARRY_I32, MATCH(I<OPCODE_DID_CARRY, I8<>, I32<>>)) {
|
EMITTER(DID_CARRY_I32, MATCH(I<OPCODE_DID_CARRY, I8<>, I32<>>)) {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
XEASSERT(!i.src1.is_constant);
|
||||||
e.LoadEflags();
|
e.LoadEflags();
|
||||||
e.setc(i.dest);
|
e.setc(i.dest);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
EMITTER(DID_CARRY_I64, MATCH(I<OPCODE_DID_CARRY, I8<>, I64<>>)) {
|
EMITTER(DID_CARRY_I64, MATCH(I<OPCODE_DID_CARRY, I8<>, I64<>>)) {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
XEASSERT(!i.src1.is_constant);
|
||||||
e.LoadEflags();
|
e.LoadEflags();
|
||||||
e.setc(i.dest);
|
e.setc(i.dest);
|
||||||
}
|
}
|
||||||
|
@ -4414,8 +4418,8 @@ EMITTER(EXTRACT_I8, MATCH(I<OPCODE_EXTRACT, I8<>, V128<>, I8<>>)) {
|
||||||
} else {
|
} else {
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
// TODO(benvanik): try out hlide's version:
|
// TODO(benvanik): try out hlide's version:
|
||||||
// mov eax, 0x80808080
|
// mov eax, 0x80808003
|
||||||
// mov al, i.src2
|
// xor al, i.src2.cvt8()
|
||||||
// vmovd xmm0, eax
|
// vmovd xmm0, eax
|
||||||
// vpshufb xmm0, i.src1, xmm0
|
// vpshufb xmm0, i.src1, xmm0
|
||||||
// vmovd i.dest.reg().cvt32(), xmm0
|
// vmovd i.dest.reg().cvt32(), xmm0
|
||||||
|
@ -4430,6 +4434,7 @@ EMITTER(EXTRACT_I16, MATCH(I<OPCODE_EXTRACT, I16<>, V128<>, I8<>>)) {
|
||||||
// TODO(benvanik): try out hlide's version:
|
// TODO(benvanik): try out hlide's version:
|
||||||
// xor eax, eax
|
// xor eax, eax
|
||||||
// mov al, i.src2 // eax = [i, 0, 0, 0]
|
// mov al, i.src2 // eax = [i, 0, 0, 0]
|
||||||
|
// xor eax, 0x80800203
|
||||||
// imul eax, eax, 0x00000202 // [i*2, i*2, 0, 0] supposedly that 0<= i < 8
|
// imul eax, eax, 0x00000202 // [i*2, i*2, 0, 0] supposedly that 0<= i < 8
|
||||||
// add eax,0x80800100 // [i*2+0b00, i*2+0b01, 0x80, 0x80]
|
// add eax,0x80800100 // [i*2+0b00, i*2+0b01, 0x80, 0x80]
|
||||||
// vmovd xmm0, eax
|
// vmovd xmm0, eax
|
||||||
|
@ -4454,7 +4459,7 @@ EMITTER(EXTRACT_I32, MATCH(I<OPCODE_EXTRACT, I32<>, V128<>, I8<>>)) {
|
||||||
// xor eax, eax
|
// xor eax, eax
|
||||||
// mov al, i.src2 // eax = [i, 0, 0, 0]
|
// mov al, i.src2 // eax = [i, 0, 0, 0]
|
||||||
// imul eax, eax, 0x04040404 // [i*4, i*4, i*4, i*4] supposedly that 0<= i < 4
|
// imul eax, eax, 0x04040404 // [i*4, i*4, i*4, i*4] supposedly that 0<= i < 4
|
||||||
// add eax,0x03020100 // [i*4+0b00, i*4+0b01, i*4+0b10, i*4+0b11]
|
// xor/add eax, 0x00010203 // [i*4+0b00, i*4+0b01, i*4+0b10, i*4+0b11]
|
||||||
// vmovd xmm0, eax
|
// vmovd xmm0, eax
|
||||||
// vpshufb xmm0, i.src1, xmm0
|
// vpshufb xmm0, i.src1, xmm0
|
||||||
// vmovd i.dest.reg().cvt32(), xmm0
|
// vmovd i.dest.reg().cvt32(), xmm0
|
||||||
|
|
|
@ -43,6 +43,14 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
// v1 = add 1000, 1000
|
// v1 = add 1000, 1000
|
||||||
// store_context +200, 2000
|
// store_context +200, 2000
|
||||||
// A DCE run after this should clean up any of the values no longer needed.
|
// A DCE run after this should clean up any of the values no longer needed.
|
||||||
|
//
|
||||||
|
// Special care needs to be taken with paired instructions. For example,
|
||||||
|
// DID_CARRY needs to be set as a constant:
|
||||||
|
// v1 = sub.2 20, 1
|
||||||
|
// v2 = did_carry v1
|
||||||
|
// should become:
|
||||||
|
// v1 = 19
|
||||||
|
// v2 = 0
|
||||||
|
|
||||||
Block* block = builder->first_block();
|
Block* block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
|
@ -252,19 +260,41 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPCODE_DID_CARRY:
|
||||||
|
XEASSERT(!i->src1.value->IsConstant());
|
||||||
|
break;
|
||||||
|
case OPCODE_DID_OVERFLOW:
|
||||||
|
XEASSERT(!i->src1.value->IsConstant());
|
||||||
|
break;
|
||||||
|
case OPCODE_DID_SATURATE:
|
||||||
|
XEASSERT(!i->src1.value->IsConstant());
|
||||||
|
break;
|
||||||
|
|
||||||
case OPCODE_ADD:
|
case OPCODE_ADD:
|
||||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||||
v->set_from(i->src1.value);
|
v->set_from(i->src1.value);
|
||||||
v->Add(i->src2.value);
|
bool did_carry = v->Add(i->src2.value);
|
||||||
|
bool propagate_carry = !!(i->flags & ARITHMETIC_SET_CARRY);
|
||||||
i->Remove();
|
i->Remove();
|
||||||
|
|
||||||
|
// If carry is set find the DID_CARRY and fix it.
|
||||||
|
if (propagate_carry) {
|
||||||
|
PropagateCarry(v, did_carry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// TODO(benvanik): ADD_CARRY
|
// TODO(benvanik): ADD_CARRY (w/ ARITHMETIC_SET_CARRY)
|
||||||
case OPCODE_SUB:
|
case OPCODE_SUB:
|
||||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||||
v->set_from(i->src1.value);
|
v->set_from(i->src1.value);
|
||||||
v->Sub(i->src2.value);
|
bool did_carry = v->Sub(i->src2.value);
|
||||||
|
bool propagate_carry = !!(i->flags & ARITHMETIC_SET_CARRY);
|
||||||
i->Remove();
|
i->Remove();
|
||||||
|
|
||||||
|
// If carry is set find the DID_CARRY and fix it.
|
||||||
|
if (propagate_carry) {
|
||||||
|
PropagateCarry(v, did_carry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OPCODE_MUL:
|
case OPCODE_MUL:
|
||||||
|
@ -393,3 +423,16 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConstantPropagationPass::PropagateCarry(hir::Value* v, bool did_carry) {
|
||||||
|
auto next = v->use_head;
|
||||||
|
while (next) {
|
||||||
|
auto use = next;
|
||||||
|
next = use->next;
|
||||||
|
if (use->instr->opcode == &OPCODE_DID_CARRY_info) {
|
||||||
|
// Replace carry value.
|
||||||
|
use->instr->dest->set_constant(did_carry ? 1 : 0);
|
||||||
|
use->instr->Remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
virtual int Run(hir::HIRBuilder* builder);
|
virtual int Run(hir::HIRBuilder* builder);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void PropagateCarry(hir::Value* v, bool did_carry);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -187,19 +187,26 @@ void Value::Round(RoundMode round_mode) {
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::Add(Value* other) {
|
bool Value::Add(Value* other) {
|
||||||
|
#define CHECK_DID_CARRY(v1, v2) (((uint64_t)v2) > ~((uint64_t)v1))
|
||||||
|
#define ADD_DID_CARRY(a, b) CHECK_DID_CARRY(a, b)
|
||||||
XEASSERT(type == other->type);
|
XEASSERT(type == other->type);
|
||||||
|
bool did_carry = false;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case INT8_TYPE:
|
case INT8_TYPE:
|
||||||
|
did_carry = ADD_DID_CARRY(constant.i8, other->constant.i8);
|
||||||
constant.i8 += other->constant.i8;
|
constant.i8 += other->constant.i8;
|
||||||
break;
|
break;
|
||||||
case INT16_TYPE:
|
case INT16_TYPE:
|
||||||
|
did_carry = ADD_DID_CARRY(constant.i16, other->constant.i16);
|
||||||
constant.i16 += other->constant.i16;
|
constant.i16 += other->constant.i16;
|
||||||
break;
|
break;
|
||||||
case INT32_TYPE:
|
case INT32_TYPE:
|
||||||
|
did_carry = ADD_DID_CARRY(constant.i32, other->constant.i32);
|
||||||
constant.i32 += other->constant.i32;
|
constant.i32 += other->constant.i32;
|
||||||
break;
|
break;
|
||||||
case INT64_TYPE:
|
case INT64_TYPE:
|
||||||
|
did_carry = ADD_DID_CARRY(constant.i64, other->constant.i64);
|
||||||
constant.i64 += other->constant.i64;
|
constant.i64 += other->constant.i64;
|
||||||
break;
|
break;
|
||||||
case FLOAT32_TYPE:
|
case FLOAT32_TYPE:
|
||||||
|
@ -212,21 +219,28 @@ void Value::Add(Value* other) {
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return did_carry;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::Sub(Value* other) {
|
bool Value::Sub(Value* other) {
|
||||||
|
#define SUB_DID_CARRY(a, b) (b > a)
|
||||||
XEASSERT(type == other->type);
|
XEASSERT(type == other->type);
|
||||||
|
bool did_carry = false;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case INT8_TYPE:
|
case INT8_TYPE:
|
||||||
|
did_carry = SUB_DID_CARRY(constant.i8, other->constant.i8);
|
||||||
constant.i8 -= other->constant.i8;
|
constant.i8 -= other->constant.i8;
|
||||||
break;
|
break;
|
||||||
case INT16_TYPE:
|
case INT16_TYPE:
|
||||||
|
did_carry = SUB_DID_CARRY(constant.i16, other->constant.i16);
|
||||||
constant.i16 -= other->constant.i16;
|
constant.i16 -= other->constant.i16;
|
||||||
break;
|
break;
|
||||||
case INT32_TYPE:
|
case INT32_TYPE:
|
||||||
|
did_carry = SUB_DID_CARRY(constant.i32, other->constant.i32);
|
||||||
constant.i32 -= other->constant.i32;
|
constant.i32 -= other->constant.i32;
|
||||||
break;
|
break;
|
||||||
case INT64_TYPE:
|
case INT64_TYPE:
|
||||||
|
did_carry = SUB_DID_CARRY(constant.i64, other->constant.i64);
|
||||||
constant.i64 -= other->constant.i64;
|
constant.i64 -= other->constant.i64;
|
||||||
break;
|
break;
|
||||||
case FLOAT32_TYPE:
|
case FLOAT32_TYPE:
|
||||||
|
@ -239,6 +253,7 @@ void Value::Sub(Value* other) {
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return did_carry;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::Mul(Value* other) {
|
void Value::Mul(Value* other) {
|
||||||
|
|
|
@ -375,8 +375,8 @@ public:
|
||||||
void Truncate(TypeName target_type);
|
void Truncate(TypeName target_type);
|
||||||
void Convert(TypeName target_type, RoundMode round_mode);
|
void Convert(TypeName target_type, RoundMode round_mode);
|
||||||
void Round(RoundMode round_mode);
|
void Round(RoundMode round_mode);
|
||||||
void Add(Value* other);
|
bool Add(Value* other);
|
||||||
void Sub(Value* other);
|
bool Sub(Value* other);
|
||||||
void Mul(Value* other);
|
void Mul(Value* other);
|
||||||
void Div(Value* other);
|
void Div(Value* other);
|
||||||
static void MulAdd(Value* dest, Value* value1, Value* value2, Value* value3);
|
static void MulAdd(Value* dest, Value* value1, Value* value2, Value* value3);
|
||||||
|
|
Loading…
Reference in New Issue