SkVMTest.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. /*
  2. * Copyright 2019 Google LLC
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "include/core/SkColorPriv.h"
  8. #include "include/private/SkColorData.h"
  9. #include "src/core/SkVM.h"
  10. #include "tests/Test.h"
  11. #include "tools/Resources.h"
  12. #include "tools/SkVMBuilders.h"
  13. using Fmt = SrcoverBuilder_F32::Fmt;
  14. const char* fmt_name(Fmt fmt) {
  15. switch (fmt) {
  16. case Fmt::A8: return "A8";
  17. case Fmt::G8: return "G8";
  18. case Fmt::RGBA_8888: return "RGBA_8888";
  19. }
  20. return "";
  21. }
  22. namespace {
  23. using namespace skvm;
  24. struct V { Val id; };
  25. struct R { Reg id; };
  26. struct Shift { int bits; };
  27. struct Splat { int bits; };
  28. struct Hex { int bits; };
  29. static void write(SkWStream* o, const char* s) {
  30. o->writeText(s);
  31. }
  32. static void write(SkWStream* o, Arg a) {
  33. write(o, "arg(");
  34. o->writeDecAsText(a.ix);
  35. write(o, ")");
  36. }
  37. static void write(SkWStream* o, V v) {
  38. write(o, "v");
  39. o->writeDecAsText(v.id);
  40. }
  41. static void write(SkWStream* o, R r) {
  42. write(o, "r");
  43. o->writeDecAsText(r.id);
  44. }
  45. static void write(SkWStream* o, Shift s) {
  46. o->writeDecAsText(s.bits);
  47. }
  48. static void write(SkWStream* o, Splat s) {
  49. float f;
  50. memcpy(&f, &s.bits, 4);
  51. o->writeHexAsText(s.bits);
  52. write(o, " (");
  53. o->writeScalarAsText(f);
  54. write(o, ")");
  55. }
  56. static void write(SkWStream* o, Hex h) {
  57. o->writeHexAsText(h.bits);
  58. }
  59. template <typename T, typename... Ts>
  60. static void write(SkWStream* o, T first, Ts... rest) {
  61. write(o, first);
  62. write(o, " ");
  63. write(o, rest...);
  64. }
  65. static void dump_builder(const Builder& builder, SkWStream* o) {
  66. const std::vector<Builder::Instruction> program = builder.program();
  67. o->writeDecAsText(program.size());
  68. o->writeText(" values:\n");
  69. for (Val id = 0; id < (Val)program.size(); id++) {
  70. const Builder::Instruction& inst = program[id];
  71. Op op = inst.op;
  72. Val x = inst.x,
  73. y = inst.y,
  74. z = inst.z;
  75. int imm = inst.imm;
  76. write(o, inst.death == 0 ? "☠️ " :
  77. inst.hoist ? "↑ " : " ");
  78. switch (op) {
  79. case Op::store8: write(o, "store8" , Arg{imm}, V{x}); break;
  80. case Op::store32: write(o, "store32", Arg{imm}, V{x}); break;
  81. case Op::load8: write(o, V{id}, "= load8" , Arg{imm}); break;
  82. case Op::load32: write(o, V{id}, "= load32", Arg{imm}); break;
  83. case Op::splat: write(o, V{id}, "= splat", Splat{imm}); break;
  84. case Op::add_f32: write(o, V{id}, "= add_f32", V{x}, V{y} ); break;
  85. case Op::sub_f32: write(o, V{id}, "= sub_f32", V{x}, V{y} ); break;
  86. case Op::mul_f32: write(o, V{id}, "= mul_f32", V{x}, V{y} ); break;
  87. case Op::div_f32: write(o, V{id}, "= div_f32", V{x}, V{y} ); break;
  88. case Op::mad_f32: write(o, V{id}, "= mad_f32", V{x}, V{y}, V{z}); break;
  89. case Op::add_i32: write(o, V{id}, "= add_i32", V{x}, V{y}); break;
  90. case Op::sub_i32: write(o, V{id}, "= sub_i32", V{x}, V{y}); break;
  91. case Op::mul_i32: write(o, V{id}, "= mul_i32", V{x}, V{y}); break;
  92. case Op::sub_i16x2: write(o, V{id}, "= sub_i16x2", V{x}, V{y}); break;
  93. case Op::mul_i16x2: write(o, V{id}, "= mul_i16x2", V{x}, V{y}); break;
  94. case Op::shr_i16x2: write(o, V{id}, "= shr_i16x2", V{x}, Shift{imm}); break;
  95. case Op::bit_and : write(o, V{id}, "= bit_and" , V{x}, V{y}); break;
  96. case Op::bit_or : write(o, V{id}, "= bit_or" , V{x}, V{y}); break;
  97. case Op::bit_xor : write(o, V{id}, "= bit_xor" , V{x}, V{y}); break;
  98. case Op::bit_clear: write(o, V{id}, "= bit_clear", V{x}, V{y}); break;
  99. case Op::shl: write(o, V{id}, "= shl", V{x}, Shift{imm}); break;
  100. case Op::shr: write(o, V{id}, "= shr", V{x}, Shift{imm}); break;
  101. case Op::sra: write(o, V{id}, "= sra", V{x}, Shift{imm}); break;
  102. case Op::extract: write(o, V{id}, "= extract", V{x}, Shift{imm}, V{y}); break;
  103. case Op::pack: write(o, V{id}, "= pack", V{x}, V{y}, Shift{imm}); break;
  104. case Op::bytes: write(o, V{id}, "= bytes", V{x}, Hex{imm}); break;
  105. case Op::to_f32: write(o, V{id}, "= to_f32", V{x}); break;
  106. case Op::to_i32: write(o, V{id}, "= to_i32", V{x}); break;
  107. }
  108. write(o, "\n");
  109. }
  110. }
  111. static void dump_program(const Program& program, SkWStream* o) {
  112. const std::vector<Program::Instruction> instructions = program.instructions();
  113. const int nregs = program.nregs();
  114. const int loop = program.loop();
  115. o->writeDecAsText(nregs);
  116. o->writeText(" registers, ");
  117. o->writeDecAsText(instructions.size());
  118. o->writeText(" instructions:\n");
  119. for (int i = 0; i < (int)instructions.size(); i++) {
  120. if (i == loop) {
  121. write(o, "loop:\n");
  122. }
  123. const Program::Instruction& inst = instructions[i];
  124. Op op = inst.op;
  125. Reg d = inst.d,
  126. x = inst.x,
  127. y = inst.y,
  128. z = inst.z;
  129. int imm = inst.imm;
  130. switch (op) {
  131. case Op::store8: write(o, "store8" , Arg{imm}, R{x}); break;
  132. case Op::store32: write(o, "store32", Arg{imm}, R{x}); break;
  133. case Op::load8: write(o, R{d}, "= load8" , Arg{imm}); break;
  134. case Op::load32: write(o, R{d}, "= load32", Arg{imm}); break;
  135. case Op::splat: write(o, R{d}, "= splat", Splat{imm}); break;
  136. case Op::add_f32: write(o, R{d}, "= add_f32", R{x}, R{y} ); break;
  137. case Op::sub_f32: write(o, R{d}, "= sub_f32", R{x}, R{y} ); break;
  138. case Op::mul_f32: write(o, R{d}, "= mul_f32", R{x}, R{y} ); break;
  139. case Op::div_f32: write(o, R{d}, "= div_f32", R{x}, R{y} ); break;
  140. case Op::mad_f32: write(o, R{d}, "= mad_f32", R{x}, R{y}, R{z}); break;
  141. case Op::add_i32: write(o, R{d}, "= add_i32", R{x}, R{y}); break;
  142. case Op::sub_i32: write(o, R{d}, "= sub_i32", R{x}, R{y}); break;
  143. case Op::mul_i32: write(o, R{d}, "= mul_i32", R{x}, R{y}); break;
  144. case Op::sub_i16x2: write(o, R{d}, "= sub_i16x2", R{x}, R{y}); break;
  145. case Op::mul_i16x2: write(o, R{d}, "= mul_i16x2", R{x}, R{y}); break;
  146. case Op::shr_i16x2: write(o, R{d}, "= shr_i16x2", R{x}, Shift{imm}); break;
  147. case Op::bit_and : write(o, R{d}, "= bit_and" , R{x}, R{y}); break;
  148. case Op::bit_or : write(o, R{d}, "= bit_or" , R{x}, R{y}); break;
  149. case Op::bit_xor : write(o, R{d}, "= bit_xor" , R{x}, R{y}); break;
  150. case Op::bit_clear: write(o, R{d}, "= bit_clear", R{x}, R{y}); break;
  151. case Op::shl: write(o, R{d}, "= shl", R{x}, Shift{imm}); break;
  152. case Op::shr: write(o, R{d}, "= shr", R{x}, Shift{imm}); break;
  153. case Op::sra: write(o, R{d}, "= sra", R{x}, Shift{imm}); break;
  154. case Op::extract: write(o, R{d}, "= extract", R{x}, Shift{imm}, R{y}); break;
  155. case Op::pack: write(o, R{d}, "= pack", R{x}, R{y}, Shift{imm}); break;
  156. case Op::bytes: write(o, R{d}, "= bytes", R{x}, Hex{imm}); break;
  157. case Op::to_f32: write(o, R{d}, "= to_f32", R{x}); break;
  158. case Op::to_i32: write(o, R{d}, "= to_i32", R{x}); break;
  159. }
  160. write(o, "\n");
  161. }
  162. }
  163. static void dump(Builder& builder, SkWStream* o) {
  164. skvm::Program program = builder.done();
  165. dump_builder(builder, o);
  166. o->writeText("\n");
  167. dump_program(program, o);
  168. o->writeText("\n");
  169. }
  170. } // namespace
  171. template <typename Fn>
  172. static void test_jit_and_interpreter(skvm::Program&& program, Fn&& test) {
  173. test((const skvm::Program&) program);
  174. program.dropJIT();
  175. test((const skvm::Program&) program);
  176. }
  177. DEF_TEST(SkVM, r) {
  178. SkDynamicMemoryWStream buf;
  179. // Write all combinations of SrcoverBuilder_F32
  180. for (int s = 0; s < 3; s++)
  181. for (int d = 0; d < 3; d++) {
  182. auto srcFmt = (Fmt)s,
  183. dstFmt = (Fmt)d;
  184. SrcoverBuilder_F32 builder{srcFmt, dstFmt};
  185. buf.writeText(fmt_name(srcFmt));
  186. buf.writeText(" over ");
  187. buf.writeText(fmt_name(dstFmt));
  188. buf.writeText("\n");
  189. dump(builder, &buf);
  190. }
  191. // Write the I32 Srcovers also.
  192. {
  193. SrcoverBuilder_I32_Naive builder;
  194. buf.writeText("I32 (Naive) 8888 over 8888\n");
  195. dump(builder, &buf);
  196. }
  197. {
  198. SrcoverBuilder_I32 builder;
  199. buf.writeText("I32 8888 over 8888\n");
  200. dump(builder, &buf);
  201. }
  202. {
  203. SrcoverBuilder_I32_SWAR builder;
  204. buf.writeText("I32 (SWAR) 8888 over 8888\n");
  205. dump(builder, &buf);
  206. }
  207. sk_sp<SkData> blob = buf.detachAsData();
  208. {
  209. sk_sp<SkData> expected = GetResourceAsData("SkVMTest.expected");
  210. REPORTER_ASSERT(r, expected, "Couldn't load SkVMTest.expected.");
  211. if (expected) {
  212. if (blob->size() != expected->size()
  213. || 0 != memcmp(blob->data(), expected->data(), blob->size())) {
  214. ERRORF(r, "SkVMTest expected\n%.*s\nbut got\n%.*s\n",
  215. expected->size(), expected->data(),
  216. blob->size(), blob->data());
  217. }
  218. SkFILEWStream out(GetResourcePath("SkVMTest.expected").c_str());
  219. if (out.isValid()) {
  220. out.write(blob->data(), blob->size());
  221. }
  222. }
  223. }
  224. auto test_8888 = [&](skvm::Program&& program) {
  225. uint32_t src[9];
  226. uint32_t dst[SK_ARRAY_COUNT(src)];
  227. test_jit_and_interpreter(std::move(program), [&](const skvm::Program& program) {
  228. for (int i = 0; i < (int)SK_ARRAY_COUNT(src); i++) {
  229. src[i] = 0xbb007733;
  230. dst[i] = 0xffaaccee;
  231. }
  232. SkPMColor expected = SkPMSrcOver(src[0], dst[0]); // 0xff2dad73
  233. program.eval((int)SK_ARRAY_COUNT(src), src, dst);
  234. // dst is probably 0xff2dad72.
  235. for (auto got : dst) {
  236. auto want = expected;
  237. for (int i = 0; i < 4; i++) {
  238. uint8_t d = got & 0xff,
  239. w = want & 0xff;
  240. if (abs(d-w) >= 2) {
  241. SkDebugf("d %02x, w %02x\n", d,w);
  242. }
  243. REPORTER_ASSERT(r, abs(d-w) < 2);
  244. got >>= 8;
  245. want >>= 8;
  246. }
  247. }
  248. });
  249. };
  250. test_8888(SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::RGBA_8888}.done("srcover_f32"));
  251. test_8888(SrcoverBuilder_I32_Naive{}.done("srcover_i32_naive"));
  252. test_8888(SrcoverBuilder_I32{}.done("srcover_i32"));
  253. test_8888(SrcoverBuilder_I32_SWAR{}.done("srcover_i32_SWAR"));
  254. test_jit_and_interpreter(SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::G8}.done(),
  255. [&](const skvm::Program& program) {
  256. uint32_t src[9];
  257. uint8_t dst[SK_ARRAY_COUNT(src)];
  258. for (int i = 0; i < (int)SK_ARRAY_COUNT(src); i++) {
  259. src[i] = 0xbb007733;
  260. dst[i] = 0x42;
  261. }
  262. SkPMColor over = SkPMSrcOver(SkPackARGB32(0xbb, 0x33, 0x77, 0x00),
  263. 0xff424242);
  264. uint8_t want = SkComputeLuminance(SkGetPackedR32(over),
  265. SkGetPackedG32(over),
  266. SkGetPackedB32(over));
  267. program.eval((int)SK_ARRAY_COUNT(src), src, dst);
  268. for (auto got : dst) {
  269. REPORTER_ASSERT(r, abs(got-want) < 3);
  270. }
  271. });
  272. test_jit_and_interpreter(SrcoverBuilder_F32{Fmt::A8, Fmt::A8}.done(),
  273. [&](const skvm::Program& program) {
  274. uint8_t src[256],
  275. dst[256];
  276. for (int i = 0; i < 256; i++) {
  277. src[i] = 255 - i;
  278. dst[i] = i;
  279. }
  280. program.eval(256, src, dst);
  281. for (int i = 0; i < 256; i++) {
  282. uint8_t want = SkGetPackedA32(SkPMSrcOver(SkPackARGB32(src[i], 0,0,0),
  283. SkPackARGB32( i, 0,0,0)));
  284. REPORTER_ASSERT(r, abs(dst[i]-want) < 2);
  285. }
  286. });
  287. }
  288. DEF_TEST(SkVM_LoopCounts, r) {
  289. // Make sure we cover all the exact N we want.
  290. // buf[i] += 1
  291. skvm::Builder b;
  292. skvm::Arg arg = b.arg<int>();
  293. b.store32(arg,
  294. b.add(b.splat(1),
  295. b.load32(arg)));
  296. test_jit_and_interpreter(b.done(), [&](const skvm::Program& program) {
  297. int buf[64];
  298. for (int N = 0; N <= (int)SK_ARRAY_COUNT(buf); N++) {
  299. for (int i = 0; i < (int)SK_ARRAY_COUNT(buf); i++) {
  300. buf[i] = i;
  301. }
  302. program.eval(N, buf);
  303. for (int i = 0; i < N; i++) {
  304. REPORTER_ASSERT(r, buf[i] == i+1);
  305. }
  306. for (int i = N; i < (int)SK_ARRAY_COUNT(buf); i++) {
  307. REPORTER_ASSERT(r, buf[i] == i);
  308. }
  309. }
  310. });
  311. }
  312. DEF_TEST(SkVM_mad, r) {
  313. // This program is designed to exercise the tricky corners of instruction
  314. // and register selection for Op::mad_f32.
  315. skvm::Builder b;
  316. {
  317. skvm::Arg arg = b.arg<int>();
  318. skvm::F32 x = b.to_f32(b.load32(arg)),
  319. y = b.mad(x,x,x), // x is needed in the future, so r[x] != r[y].
  320. z = b.mad(y,y,x), // y is needed in the future, but r[z] = r[x] is ok.
  321. w = b.mad(z,z,y), // w can alias z but not y.
  322. v = b.mad(w,y,w); // Got to stop somewhere.
  323. b.store32(arg, b.to_i32(v));
  324. }
  325. test_jit_and_interpreter(b.done(), [&](const skvm::Program& program) {
  326. int x = 2;
  327. program.eval(1, &x);
  328. // x = 2
  329. // y = 2*2 + 2 = 6
  330. // z = 6*6 + 2 = 38
  331. // w = 38*38 + 6 = 1450
  332. // v = 1450*6 + 1450 = 10150
  333. REPORTER_ASSERT(r, x == 10150);
  334. });
  335. }
  336. DEF_TEST(SkVM_hoist, r) {
  337. // This program uses enough constants that it will fail to JIT if we hoist them.
  338. // The JIT will try again without hoisting, and that'll just need 2 registers.
  339. skvm::Builder b;
  340. {
  341. skvm::Arg arg = b.arg<int>();
  342. skvm::I32 x = b.load32(arg);
  343. for (int i = 0; i < 32; i++) {
  344. x = b.add(x, b.splat(i));
  345. }
  346. b.store32(arg, x);
  347. }
  348. test_jit_and_interpreter(b.done(), [&](const skvm::Program& program) {
  349. int x = 4;
  350. program.eval(1, &x);
  351. // x += 0 + 1 + 2 + 3 + ... + 30 + 31
  352. // x += 496
  353. REPORTER_ASSERT(r, x == 500);
  354. });
  355. }
  356. template <typename Fn>
  357. static void test_asm(skiatest::Reporter* r, Fn&& fn, std::initializer_list<uint8_t> expected) {
  358. uint8_t buf[4096];
  359. skvm::Assembler a{buf};
  360. fn(a);
  361. REPORTER_ASSERT(r, a.size() == expected.size());
  362. auto got = (const uint8_t*)buf,
  363. want = expected.begin();
  364. for (int i = 0; i < (int)std::min(a.size(), expected.size()); i++) {
  365. REPORTER_ASSERT(r, got[i] == want[i],
  366. "byte %d was %02x, want %02x", i, got[i], want[i]);
  367. }
  368. }
  369. DEF_TEST(SkVM_Assembler, r) {
  370. // Easiest way to generate test cases is
  371. //
  372. // echo '...some asm...' | llvm-mc -show-encoding -x86-asm-syntax=intel
  373. //
  374. // The -x86-asm-syntax=intel bit is optional, controlling the
  375. // input syntax only; the output will always be AT&T op x,y,dst style.
  376. // Our APIs read more like Intel op dst,x,y as op(dst,x,y), so I find
  377. // that a bit easier to use here, despite maybe favoring AT&T overall.
  378. using A = skvm::Assembler;
  379. // Our exit strategy from AVX code.
  380. test_asm(r, [&](A& a) {
  381. a.vzeroupper();
  382. a.ret();
  383. },{
  384. 0xc5, 0xf8, 0x77,
  385. 0xc3,
  386. });
  387. // Align should pad with zero
  388. test_asm(r, [&](A& a) {
  389. a.ret();
  390. a.align(4);
  391. },{
  392. 0xc3,
  393. 0x00, 0x00, 0x00,
  394. });
  395. test_asm(r, [&](A& a) {
  396. a.add(A::rax, 8); // Always good to test rax.
  397. a.sub(A::rax, 32);
  398. a.add(A::rdi, 12); // Last 0x48 REX
  399. a.sub(A::rdi, 8);
  400. a.add(A::r8 , 7); // First 0x49 REX
  401. a.sub(A::r8 , 4);
  402. a.add(A::rsi, 128); // Requires 4 byte immediate.
  403. a.sub(A::r8 , 1000000);
  404. },{
  405. 0x48, 0x83, 0b11'000'000, 0x08,
  406. 0x48, 0x83, 0b11'101'000, 0x20,
  407. 0x48, 0x83, 0b11'000'111, 0x0c,
  408. 0x48, 0x83, 0b11'101'111, 0x08,
  409. 0x49, 0x83, 0b11'000'000, 0x07,
  410. 0x49, 0x83, 0b11'101'000, 0x04,
  411. 0x48, 0x81, 0b11'000'110, 0x80, 0x00, 0x00, 0x00,
  412. 0x49, 0x81, 0b11'101'000, 0x40, 0x42, 0x0f, 0x00,
  413. });
  414. test_asm(r, [&](A& a) {
  415. a.vpaddd (A::ymm0, A::ymm1, A::ymm2); // Low registers and 0x0f map -> 2-byte VEX.
  416. a.vpaddd (A::ymm8, A::ymm1, A::ymm2); // A high dst register is ok -> 2-byte VEX.
  417. a.vpaddd (A::ymm0, A::ymm8, A::ymm2); // A high first argument register -> 2-byte VEX.
  418. a.vpaddd (A::ymm0, A::ymm1, A::ymm8); // A high second argument -> 3-byte VEX.
  419. a.vpmulld(A::ymm0, A::ymm1, A::ymm2); // Using non-0x0f map instruction -> 3-byte VEX.
  420. a.vpsubd (A::ymm0, A::ymm1, A::ymm2); // Test vpsubd to ensure argument order is right.
  421. },{
  422. /* VEX */ /*op*/ /*modRM*/
  423. 0xc5, 0xf5, 0xfe, 0xc2,
  424. 0xc5, 0x75, 0xfe, 0xc2,
  425. 0xc5, 0xbd, 0xfe, 0xc2,
  426. 0xc4, 0xc1, 0x75, 0xfe, 0xc0,
  427. 0xc4, 0xe2, 0x75, 0x40, 0xc2,
  428. 0xc5, 0xf5, 0xfa, 0xc2,
  429. });
  430. test_asm(r, [&](A& a) {
  431. a.vpsrld(A::ymm15, A::ymm2, 8);
  432. a.vpsrld(A::ymm0 , A::ymm8, 5);
  433. },{
  434. 0xc5, 0x85, 0x72,0xd2, 0x08,
  435. 0xc4,0xc1,0x7d, 0x72,0xd0, 0x05,
  436. });
  437. test_asm(r, [&](A& a) {
  438. a.vpermq(A::ymm1, A::ymm2, 5);
  439. },{
  440. 0xc4,0xe3,0xfd, 0x00,0xca, 0x05,
  441. });
  442. test_asm(r, [&](A& a) {
  443. A::Label l = a.here();
  444. a.byte(1);
  445. a.byte(2);
  446. a.byte(3);
  447. a.byte(4);
  448. a.vbroadcastss(A::ymm0 , &l);
  449. a.vbroadcastss(A::ymm1 , &l);
  450. a.vbroadcastss(A::ymm8 , &l);
  451. a.vbroadcastss(A::ymm15, &l);
  452. a.vpshufb(A::ymm4, A::ymm3, &l);
  453. },{
  454. 0x01, 0x02, 0x03, 0x4,
  455. /* VEX */ /*op*/ /* ModRM */ /* offset */
  456. 0xc4, 0xe2, 0x7d, 0x18, 0b00'000'101, 0xf3,0xff,0xff,0xff, // 0xfffffff3 == -13
  457. 0xc4, 0xe2, 0x7d, 0x18, 0b00'001'101, 0xea,0xff,0xff,0xff, // 0xffffffea == -22
  458. 0xc4, 0x62, 0x7d, 0x18, 0b00'000'101, 0xe1,0xff,0xff,0xff, // 0xffffffe1 == -31
  459. 0xc4, 0x62, 0x7d, 0x18, 0b00'111'101, 0xd8,0xff,0xff,0xff, // 0xffffffd8 == -40
  460. 0xc4, 0xe2, 0x65, 0x00, 0b00'100'101, 0xcf,0xff,0xff,0xff, // 0xffffffcf == -49
  461. });
  462. test_asm(r, [&](A& a) {
  463. A::Label l = a.here();
  464. a.jne(&l);
  465. a.jne(&l);
  466. a.je (&l);
  467. a.jmp(&l);
  468. a.jl (&l);
  469. a.cmp(A::rdx, 0);
  470. a.cmp(A::rax, 12);
  471. a.cmp(A::r14, 2000000000);
  472. },{
  473. 0x0f,0x85, 0xfa,0xff,0xff,0xff, // near jne -6 bytes
  474. 0x0f,0x85, 0xf4,0xff,0xff,0xff, // near jne -12 bytes
  475. 0x0f,0x84, 0xee,0xff,0xff,0xff, // near je -18 bytes
  476. 0xe9, 0xe9,0xff,0xff,0xff, // near jmp -23 bytes
  477. 0x0f,0x8c, 0xe3,0xff,0xff,0xff, // near jl -29 bytes
  478. 0x48,0x83,0xfa,0x00,
  479. 0x48,0x83,0xf8,0x0c,
  480. 0x49,0x81,0xfe,0x00,0x94,0x35,0x77,
  481. });
  482. test_asm(r, [&](A& a) {
  483. a.vmovups(A::ymm5, A::rsi);
  484. a.vmovups(A::rsi, A::ymm5);
  485. a.vpmovzxbd(A::ymm4, A::rsi);
  486. a.vmovq(A::rdx, A::xmm15);
  487. },{
  488. /* VEX */ /*Op*/ /* ModRM */
  489. 0xc5, 0xfc, 0x10, 0b00'101'110,
  490. 0xc5, 0xfc, 0x11, 0b00'101'110,
  491. 0xc4,0xe2,0x7d, 0x31, 0b00'100'110,
  492. 0xc5, 0x79, 0xd6, 0b00'111'010,
  493. });
  494. test_asm(r, [&](A& a) {
  495. a.movzbl(A::rax, A::rsi); // Low registers for src and dst.
  496. a.movzbl(A::rax, A::r8); // High src register.
  497. a.movzbl(A::r8 , A::rsi); // High dst register.
  498. a.vmovd(A::rax, A::xmm0);
  499. a.vmovd(A::rax, A::xmm8);
  500. a.vmovd(A::r8, A::xmm0);
  501. a.vmovd(A::xmm0, A::rax);
  502. a.vmovd(A::xmm8, A::rax);
  503. a.vmovd(A::xmm0, A::r8);
  504. a.vmovd_direct(A::rax, A::xmm0);
  505. a.vmovd_direct(A::rax, A::xmm8);
  506. a.vmovd_direct(A::r8, A::xmm0);
  507. a.vmovd_direct(A::xmm0, A::rax);
  508. a.vmovd_direct(A::xmm8, A::rax);
  509. a.vmovd_direct(A::xmm0, A::r8);
  510. a.movb(A::rdx, A::rax);
  511. a.movb(A::rdx, A::r8);
  512. a.movb(A::r8 , A::rax);
  513. },{
  514. 0x0f,0xb6,0x06,
  515. 0x41,0x0f,0xb6,0x00,
  516. 0x44,0x0f,0xb6,0x06,
  517. 0xc5,0xf9,0x7e,0x00,
  518. 0xc5,0x79,0x7e,0x00,
  519. 0xc4,0xc1,0x79,0x7e,0x00,
  520. 0xc5,0xf9,0x6e,0x00,
  521. 0xc5,0x79,0x6e,0x00,
  522. 0xc4,0xc1,0x79,0x6e,0x00,
  523. 0xc5,0xf9,0x7e,0xc0,
  524. 0xc5,0x79,0x7e,0xc0,
  525. 0xc4,0xc1,0x79,0x7e,0xc0,
  526. 0xc5,0xf9,0x6e,0xc0,
  527. 0xc5,0x79,0x6e,0xc0,
  528. 0xc4,0xc1,0x79,0x6e,0xc0,
  529. 0x88, 0x02,
  530. 0x44, 0x88, 0x02,
  531. 0x41, 0x88, 0x00,
  532. });
  533. test_asm(r, [&](A& a) {
  534. a.vpinsrb(A::xmm1, A::xmm8, A::rsi, 4);
  535. a.vpinsrb(A::xmm8, A::xmm1, A::r8, 12);
  536. a.vpextrb(A::rsi, A::xmm8, 7);
  537. a.vpextrb(A::r8, A::xmm1, 15);
  538. },{
  539. 0xc4,0xe3,0x39, 0x20, 0x0e, 4,
  540. 0xc4,0x43,0x71, 0x20, 0x00, 12,
  541. 0xc4,0x63,0x79, 0x14, 0x06, 7,
  542. 0xc4,0xc3,0x79, 0x14, 0x08, 15,
  543. });
  544. test_asm(r, [&](A& a) {
  545. a.vpandn(A::ymm3, A::ymm12, A::ymm2);
  546. },{
  547. 0xc5, 0x9d, 0xdf, 0xda,
  548. });
  549. // echo "fmul v4.4s, v3.4s, v1.4s" | llvm-mc -show-encoding -arch arm64
  550. test_asm(r, [&](A& a) {
  551. a.and16b(A::v4, A::v3, A::v1);
  552. a.orr16b(A::v4, A::v3, A::v1);
  553. a.eor16b(A::v4, A::v3, A::v1);
  554. a.bic16b(A::v4, A::v3, A::v1);
  555. a.add4s(A::v4, A::v3, A::v1);
  556. a.sub4s(A::v4, A::v3, A::v1);
  557. a.mul4s(A::v4, A::v3, A::v1);
  558. a.sub8h(A::v4, A::v3, A::v1);
  559. a.mul8h(A::v4, A::v3, A::v1);
  560. a.fadd4s(A::v4, A::v3, A::v1);
  561. a.fsub4s(A::v4, A::v3, A::v1);
  562. a.fmul4s(A::v4, A::v3, A::v1);
  563. a.fdiv4s(A::v4, A::v3, A::v1);
  564. a.fmla4s(A::v4, A::v3, A::v1);
  565. },{
  566. 0x64,0x1c,0x21,0x4e,
  567. 0x64,0x1c,0xa1,0x4e,
  568. 0x64,0x1c,0x21,0x6e,
  569. 0x64,0x1c,0x61,0x4e,
  570. 0x64,0x84,0xa1,0x4e,
  571. 0x64,0x84,0xa1,0x6e,
  572. 0x64,0x9c,0xa1,0x4e,
  573. 0x64,0x84,0x61,0x6e,
  574. 0x64,0x9c,0x61,0x4e,
  575. 0x64,0xd4,0x21,0x4e,
  576. 0x64,0xd4,0xa1,0x4e,
  577. 0x64,0xdc,0x21,0x6e,
  578. 0x64,0xfc,0x21,0x6e,
  579. 0x64,0xcc,0x21,0x4e,
  580. });
  581. test_asm(r, [&](A& a) {
  582. a.shl4s(A::v4, A::v3, 0);
  583. a.shl4s(A::v4, A::v3, 1);
  584. a.shl4s(A::v4, A::v3, 8);
  585. a.shl4s(A::v4, A::v3, 16);
  586. a.shl4s(A::v4, A::v3, 31);
  587. a.sshr4s(A::v4, A::v3, 1);
  588. a.sshr4s(A::v4, A::v3, 8);
  589. a.sshr4s(A::v4, A::v3, 31);
  590. a.ushr4s(A::v4, A::v3, 1);
  591. a.ushr4s(A::v4, A::v3, 8);
  592. a.ushr4s(A::v4, A::v3, 31);
  593. a.ushr8h(A::v4, A::v3, 1);
  594. a.ushr8h(A::v4, A::v3, 8);
  595. a.ushr8h(A::v4, A::v3, 15);
  596. },{
  597. 0x64,0x54,0x20,0x4f,
  598. 0x64,0x54,0x21,0x4f,
  599. 0x64,0x54,0x28,0x4f,
  600. 0x64,0x54,0x30,0x4f,
  601. 0x64,0x54,0x3f,0x4f,
  602. 0x64,0x04,0x3f,0x4f,
  603. 0x64,0x04,0x38,0x4f,
  604. 0x64,0x04,0x21,0x4f,
  605. 0x64,0x04,0x3f,0x6f,
  606. 0x64,0x04,0x38,0x6f,
  607. 0x64,0x04,0x21,0x6f,
  608. 0x64,0x04,0x1f,0x6f,
  609. 0x64,0x04,0x18,0x6f,
  610. 0x64,0x04,0x11,0x6f,
  611. });
  612. test_asm(r, [&](A& a) {
  613. a.sli4s(A::v4, A::v3, 0);
  614. a.sli4s(A::v4, A::v3, 1);
  615. a.sli4s(A::v4, A::v3, 8);
  616. a.sli4s(A::v4, A::v3, 16);
  617. a.sli4s(A::v4, A::v3, 31);
  618. },{
  619. 0x64,0x54,0x20,0x6f,
  620. 0x64,0x54,0x21,0x6f,
  621. 0x64,0x54,0x28,0x6f,
  622. 0x64,0x54,0x30,0x6f,
  623. 0x64,0x54,0x3f,0x6f,
  624. });
  625. test_asm(r, [&](A& a) {
  626. a.scvtf4s (A::v4, A::v3);
  627. a.fcvtzs4s(A::v4, A::v3);
  628. },{
  629. 0x64,0xd8,0x21,0x4e,
  630. 0x64,0xb8,0xa1,0x4e,
  631. });
  632. test_asm(r, [&](A& a) {
  633. a.ret(A::x30); // Conventional ret using link register.
  634. a.ret(A::x13); // Can really return using any register if we like.
  635. a.add(A::x2, A::x2, 4);
  636. a.add(A::x3, A::x2, 32);
  637. a.sub(A::x2, A::x2, 4);
  638. a.sub(A::x3, A::x2, 32);
  639. a.subs(A::x2, A::x2, 4);
  640. a.subs(A::x3, A::x2, 32);
  641. a.subs(A::xzr, A::x2, 4); // These are actually the same instruction!
  642. a.cmp(A::x2, 4);
  643. A::Label l = a.here();
  644. a.bne(&l);
  645. a.bne(&l);
  646. a.blt(&l);
  647. a.b(&l);
  648. a.cbnz(A::x2, &l);
  649. a.cbz(A::x2, &l);
  650. },{
  651. 0xc0,0x03,0x5f,0xd6,
  652. 0xa0,0x01,0x5f,0xd6,
  653. 0x42,0x10,0x00,0x91,
  654. 0x43,0x80,0x00,0x91,
  655. 0x42,0x10,0x00,0xd1,
  656. 0x43,0x80,0x00,0xd1,
  657. 0x42,0x10,0x00,0xf1,
  658. 0x43,0x80,0x00,0xf1,
  659. 0x5f,0x10,0x00,0xf1,
  660. 0x5f,0x10,0x00,0xf1,
  661. 0x01,0x00,0x00,0x54, // b.ne #0
  662. 0xe1,0xff,0xff,0x54, // b.ne #-4
  663. 0xcb,0xff,0xff,0x54, // b.lt #-8
  664. 0xae,0xff,0xff,0x54, // b.al #-12
  665. 0x82,0xff,0xff,0xb5, // cbnz x2, #-16
  666. 0x62,0xff,0xff,0xb4, // cbz x2, #-20
  667. });
  668. // Can we cbz() to a not-yet-defined label?
  669. test_asm(r, [&](A& a) {
  670. A::Label l;
  671. a.cbz(A::x2, &l);
  672. a.add(A::x3, A::x2, 32);
  673. a.label(&l);
  674. a.ret(A::x30);
  675. },{
  676. 0x42,0x00,0x00,0xb4, // cbz x2, #8
  677. 0x43,0x80,0x00,0x91, // add x3, x2, #32
  678. 0xc0,0x03,0x5f,0xd6, // ret
  679. });
  680. // If we start a label as a backward label,
  681. // can we redefine it to be a future label?
  682. // (Not sure this is useful... just want to test it works.)
  683. test_asm(r, [&](A& a) {
  684. A::Label l1 = a.here();
  685. a.add(A::x3, A::x2, 32);
  686. a.cbz(A::x2, &l1); // This will jump backward... nothing sneaky.
  687. A::Label l2 = a.here(); // Start off the same...
  688. a.add(A::x3, A::x2, 32);
  689. a.cbz(A::x2, &l2); // Looks like this will go backward...
  690. a.add(A::x2, A::x2, 4);
  691. a.add(A::x3, A::x2, 32);
  692. a.label(&l2); // But no... actually forward! What a switcheroo!
  693. },{
  694. 0x43,0x80,0x00,0x91, // add x3, x2, #32
  695. 0xe2,0xff,0xff,0xb4, // cbz x2, #-4
  696. 0x43,0x80,0x00,0x91, // add x3, x2, #32
  697. 0x62,0x00,0x00,0xb4, // cbz x2, #12
  698. 0x42,0x10,0x00,0x91, // add x2, x2, #4
  699. 0x43,0x80,0x00,0x91, // add x3, x2, #32
  700. });
  701. test_asm(r, [&](A& a) {
  702. a.ldrq(A::v0, A::x8);
  703. a.strq(A::v0, A::x8);
  704. },{
  705. 0x00,0x01,0xc0,0x3d,
  706. 0x00,0x01,0x80,0x3d,
  707. });
  708. test_asm(r, [&](A& a) {
  709. a.xtns2h(A::v0, A::v0);
  710. a.xtnh2b(A::v0, A::v0);
  711. a.strs (A::v0, A::x0);
  712. a.ldrs (A::v0, A::x0);
  713. a.uxtlb2h(A::v0, A::v0);
  714. a.uxtlh2s(A::v0, A::v0);
  715. },{
  716. 0x00,0x28,0x61,0x0e,
  717. 0x00,0x28,0x21,0x0e,
  718. 0x00,0x00,0x00,0xbd,
  719. 0x00,0x00,0x40,0xbd,
  720. 0x00,0xa4,0x08,0x2f,
  721. 0x00,0xa4,0x10,0x2f,
  722. });
  723. test_asm(r, [&](A& a) {
  724. a.ldrb(A::v0, A::x8);
  725. a.strb(A::v0, A::x8);
  726. },{
  727. 0x00,0x01,0x40,0x3d,
  728. 0x00,0x01,0x00,0x3d,
  729. });
  730. }