implement assert_true on ARM
This all comes together as uminv tmp, condition fmov gp, tmp cbnz gp, all_true brk 0 all_true: ... The key idea is uminv(vec) will return 0 if any of the inputs are 0, and non-zero if all of the inputs are non-zero, namely 0xffffffff. fmov moves that minimum from a vector register to a general purpose register where we can test it with cbnz, compare and branch if non-zero. This jumps over the `brk 0` debug trap when all inputs are true. Change-Id: If5deb77a77f52221d0649e537179743c45eb9cc5 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/254479 Auto-Submit: Mike Klein <mtklein@google.com> Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
fce27adfc5
commit
37be7715fd
@ -1151,6 +1151,13 @@ namespace skvm {
|
|||||||
void Assembler::uxtlb2h(V d, V n) { this->op(0b0'0'1'011110'0001'000'10100'1, n,d); }
|
void Assembler::uxtlb2h(V d, V n) { this->op(0b0'0'1'011110'0001'000'10100'1, n,d); }
|
||||||
void Assembler::uxtlh2s(V d, V n) { this->op(0b0'0'1'011110'0010'000'10100'1, n,d); }
|
void Assembler::uxtlh2s(V d, V n) { this->op(0b0'0'1'011110'0010'000'10100'1, n,d); }
|
||||||
|
|
||||||
|
void Assembler::uminv4s(V d, V n) { this->op(0b0'1'1'01110'10'11000'1'1010'10, n,d); }
|
||||||
|
|
||||||
|
void Assembler::brk(int imm16) {
|
||||||
|
this->word(0b11010100'001'0000000000000000'000'00
|
||||||
|
| (imm16 & 16_mask) << 5);
|
||||||
|
}
|
||||||
|
|
||||||
void Assembler::ret(X n) {
|
void Assembler::ret(X n) {
|
||||||
this->word(0b1101011'0'0'10'11111'0000'0'0 << 10
|
this->word(0b1101011'0'0'10'11111'0000'0'0 << 10
|
||||||
| (n & 5_mask) << 5);
|
| (n & 5_mask) << 5);
|
||||||
@ -1202,6 +1209,12 @@ namespace skvm {
|
|||||||
void Assembler::strs(V src, X dst) { this->op(0b10'111'1'01'00'000000000000, dst, src); }
|
void Assembler::strs(V src, X dst) { this->op(0b10'111'1'01'00'000000000000, dst, src); }
|
||||||
void Assembler::strb(V src, X dst) { this->op(0b00'111'1'01'00'000000000000, dst, src); }
|
void Assembler::strb(V src, X dst) { this->op(0b00'111'1'01'00'000000000000, dst, src); }
|
||||||
|
|
||||||
|
void Assembler::fmovs(X dst, V src) {
|
||||||
|
this->word(0b0'0'0'11110'00'1'00'110'000000 << 10
|
||||||
|
| (src & 5_mask) << 5
|
||||||
|
| (dst & 5_mask) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
void Assembler::ldrq(V dst, Label* l) {
|
void Assembler::ldrq(V dst, Label* l) {
|
||||||
const int imm19 = this->disp19(l);
|
const int imm19 = this->disp19(l);
|
||||||
this->word( 0b10'011'1'00 << 24
|
this->word( 0b10'011'1'00 << 24
|
||||||
@ -1699,16 +1712,18 @@ namespace skvm {
|
|||||||
if (!SkCpu::Supports(SkCpu::HSW)) {
|
if (!SkCpu::Supports(SkCpu::HSW)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
A::GP64 N = A::rdi,
|
A::GP64 N = A::rdi,
|
||||||
arg[] = { A::rsi, A::rdx, A::rcx, A::r8, A::r9 };
|
scratch = A::rax,
|
||||||
|
arg[] = { A::rsi, A::rdx, A::rcx, A::r8, A::r9 };
|
||||||
|
|
||||||
// All 16 ymm registers are available to use.
|
// All 16 ymm registers are available to use.
|
||||||
using Reg = A::Ymm;
|
using Reg = A::Ymm;
|
||||||
uint32_t avail = 0xffff;
|
uint32_t avail = 0xffff;
|
||||||
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
A::X N = A::x0,
|
A::X N = A::x0,
|
||||||
arg[] = { A::x1, A::x2, A::x3, A::x4, A::x5, A::x6, A::x7 };
|
scratch = A::x8,
|
||||||
|
arg[] = { A::x1, A::x2, A::x3, A::x4, A::x5, A::x6, A::x7 };
|
||||||
|
|
||||||
// We can use v0-v7 and v16-v31 freely; we'd need to preserve v8-v15.
|
// We can use v0-v7 and v16-v31 freely; we'd need to preserve v8-v15.
|
||||||
using Reg = A::V;
|
using Reg = A::V;
|
||||||
@ -1911,8 +1926,8 @@ namespace skvm {
|
|||||||
else { a->vmovups( dst(), arg[imm]); }
|
else { a->vmovups( dst(), arg[imm]); }
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Op::uniform8: a->movzbl(A::rax, arg[imm&0xffff], imm>>16);
|
case Op::uniform8: a->movzbl(scratch, arg[imm&0xffff], imm>>16);
|
||||||
a->vmovd_direct((A::Xmm)dst(), A::rax);
|
a->vmovd_direct((A::Xmm)dst(), scratch);
|
||||||
a->vbroadcastss(dst(), (A::Xmm)dst());
|
a->vbroadcastss(dst(), (A::Xmm)dst());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1989,7 +2004,14 @@ namespace skvm {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
case Op::assert_true: /*TODO somehow?*/ break;
|
case Op::assert_true: {
|
||||||
|
a->uminv4s(tmp(), r[x]); // uminv acts like an all() across the vector.
|
||||||
|
a->fmovs(scratch, tmp());
|
||||||
|
A::Label all_true;
|
||||||
|
a->cbnz(scratch, &all_true);
|
||||||
|
a->brk(0);
|
||||||
|
a->label(&all_true);
|
||||||
|
} break;
|
||||||
|
|
||||||
case Op::store8: a->xtns2h(tmp(), r[x]);
|
case Op::store8: a->xtns2h(tmp(), r[x]);
|
||||||
a->xtnh2b(tmp(), tmp());
|
a->xtnh2b(tmp(), tmp());
|
||||||
|
@ -174,8 +174,10 @@ namespace skvm {
|
|||||||
xtns2h, // u32 -> u16
|
xtns2h, // u32 -> u16
|
||||||
xtnh2b, // u16 -> u8
|
xtnh2b, // u16 -> u8
|
||||||
uxtlb2h, // u8 -> u16
|
uxtlb2h, // u8 -> u16
|
||||||
uxtlh2s; // u16 -> u32
|
uxtlh2s, // u16 -> u32
|
||||||
|
uminv4s; // dst[0] = min(n[0],n[1],n[2],n[3]), n as unsigned
|
||||||
|
|
||||||
|
void brk (int imm16);
|
||||||
void ret (X);
|
void ret (X);
|
||||||
void add (X d, X n, int imm12);
|
void add (X d, X n, int imm12);
|
||||||
void sub (X d, X n, int imm12);
|
void sub (X d, X n, int imm12);
|
||||||
@ -207,6 +209,8 @@ namespace skvm {
|
|||||||
void strs(V src, X dst); // 32-bit *dst = src
|
void strs(V src, X dst); // 32-bit *dst = src
|
||||||
void strb(V src, X dst); // 8-bit *dst = src
|
void strb(V src, X dst); // 8-bit *dst = src
|
||||||
|
|
||||||
|
void fmovs(X dst, V src); // dst = 32-bit src[0]
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// dst = op(dst, imm)
|
// dst = op(dst, imm)
|
||||||
void op(int opcode, int opcode_ext, GP64 dst, int imm);
|
void op(int opcode, int opcode_ext, GP64 dst, int imm);
|
||||||
|
@ -1199,6 +1199,9 @@ DEF_TEST(SkVM_Assembler, r) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test_asm(r, [&](A& a) {
|
test_asm(r, [&](A& a) {
|
||||||
|
a.brk(0);
|
||||||
|
a.brk(65535);
|
||||||
|
|
||||||
a.ret(A::x30); // Conventional ret using link register.
|
a.ret(A::x30); // Conventional ret using link register.
|
||||||
a.ret(A::x13); // Can really return using any register if we like.
|
a.ret(A::x13); // Can really return using any register if we like.
|
||||||
|
|
||||||
@ -1222,6 +1225,9 @@ DEF_TEST(SkVM_Assembler, r) {
|
|||||||
a.cbnz(A::x2, &l);
|
a.cbnz(A::x2, &l);
|
||||||
a.cbz(A::x2, &l);
|
a.cbz(A::x2, &l);
|
||||||
},{
|
},{
|
||||||
|
0x00,0x00,0x20,0xd4,
|
||||||
|
0xe0,0xff,0x3f,0xd4,
|
||||||
|
|
||||||
0xc0,0x03,0x5f,0xd6,
|
0xc0,0x03,0x5f,0xd6,
|
||||||
0xa0,0x01,0x5f,0xd6,
|
0xa0,0x01,0x5f,0xd6,
|
||||||
|
|
||||||
@ -1314,6 +1320,9 @@ DEF_TEST(SkVM_Assembler, r) {
|
|||||||
a.ldrs (A::v0, A::x0);
|
a.ldrs (A::v0, A::x0);
|
||||||
a.uxtlb2h(A::v0, A::v0);
|
a.uxtlb2h(A::v0, A::v0);
|
||||||
a.uxtlh2s(A::v0, A::v0);
|
a.uxtlh2s(A::v0, A::v0);
|
||||||
|
|
||||||
|
a.uminv4s(A::v3, A::v4);
|
||||||
|
a.fmovs (A::x3, A::v4); // fmov w3,s4
|
||||||
},{
|
},{
|
||||||
0x00,0x28,0x61,0x0e,
|
0x00,0x28,0x61,0x0e,
|
||||||
0x00,0x28,0x21,0x0e,
|
0x00,0x28,0x21,0x0e,
|
||||||
@ -1322,6 +1331,9 @@ DEF_TEST(SkVM_Assembler, r) {
|
|||||||
0x00,0x00,0x40,0xbd,
|
0x00,0x00,0x40,0xbd,
|
||||||
0x00,0xa4,0x08,0x2f,
|
0x00,0xa4,0x08,0x2f,
|
||||||
0x00,0xa4,0x10,0x2f,
|
0x00,0xa4,0x10,0x2f,
|
||||||
|
|
||||||
|
0x83,0xa8,0xb1,0x6e,
|
||||||
|
0x83,0x00,0x26,0x1e,
|
||||||
});
|
});
|
||||||
|
|
||||||
test_asm(r, [&](A& a) {
|
test_asm(r, [&](A& a) {
|
||||||
|
Loading…
Reference in New Issue
Block a user