Skip to content

Commit ce4cec9

Browse files
committed
DynASM/arm64: allow dynamic register names
DynASM/x86 permits register names of the form Ra(expr) where "expr" is an arbitrary C expression and "Ra" is register class. This allows registers to be selected dynamically at runtime. However the Arm64 port only accepts static register names such as "x1", "w5", etc. This patch extends the DynASM parser for arm64 to accept expressions of the form Rx(expr) where "x" is a register class such as "x", "w", "q", etc. A new action DASM_VREG patches the instruction with the dynamic register index passed as an argument (similar to how dynamic register names work on x86). To correctly patch the instruction we need to know the bit position of the register in the instruction word. This is now passed into parse_reg() and encoded in the low bits of the DASM_VREG opcode. To avoid duplication of the bit position in code like shl(parse_reg(..., 5), 5) parse_reg() now returns the shifted register index. Besides, with the introduction of dynmiac register names, the original method, i.e 'p[-2]', to accessing 'scale' field for action DASM_IMML, might be polluted. As a result, load/store with an immediate or type maps, would be affected. This patch passes 'scale' as the parameter to DASM_IMML. Example [1]: | cmp Rx(15), php#42 | mov x3, php#1 | add Rw(5+1), Rw(7), Rw(22) | ldr Rx(4), [Rx(2), php#8]! | str x0, [Rx(2), #offsetof(struct stu, age)] | ldr x8, STATE->get_ch Disassembly: 0xffffb5498000: cmp x15, #0x2a 0xffffb5498004: movz w3, #0x1 0xffffb5498008: add w6, w7, w22 0xffffb549800c: ldr x4, [x2, php#8]! 0xffffb5498010: str x0, [x2, php#8] 0xffffb5498014: ldr x8, [x2, php#8] Test environment: We're using an ARM-based server, with Ubuntu-20 and GCC-10. Disambler library capstone[2] should be installed in advance. After building the PHP, the 'minilua' can be found in 'PHP-SRC/ext/opcache/' directory. Our test case can be run with the following commands: $ PHP-SRC/ext/opcache/minilua \ PHP-SRC/ext/opcache/jit/dynasm/dynasm.lua -o test.c \ -D ARM64=1 test-dyn-regs.c $ gcc test.c -o test -lcapstone $ ./test [1] https://github.com/shqking/misc/blob/main/php-dynasm-test/test-dyn-regs.c [2] https://www.capstone-engine.org/ Co-Developed-by: Nick Gasson <Nick.Gasson@arm.com>
1 parent 202a701 commit ce4cec9

File tree

2 files changed

+36
-14
lines changed

2 files changed

+36
-14
lines changed

ext/opcache/jit/dynasm/dasm_arm64.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ enum {
2323
/* The following actions also have an argument. */
2424
DASM_REL_PC, DASM_LABEL_PC,
2525
DASM_IMM, DASM_IMM6, DASM_IMM12, DASM_IMM13W, DASM_IMM13X, DASM_IMML,
26+
DASM_VREG,
2627
DASM__MAX
2728
};
2829

@@ -39,6 +40,7 @@ enum {
3940
#define DASM_S_RANGE_LG 0x13000000
4041
#define DASM_S_RANGE_PC 0x14000000
4142
#define DASM_S_RANGE_REL 0x15000000
43+
#define DASM_S_RANGE_VREG 0x16000000
4244
#define DASM_S_UNDEF_LG 0x21000000
4345
#define DASM_S_UNDEF_PC 0x22000000
4446

@@ -312,13 +314,17 @@ void dasm_put(Dst_DECL, int start, ...)
312314
}
313315
case DASM_IMML: {
314316
#ifdef DASM_CHECKS
315-
int scale = (p[-2] >> 30);
317+
int scale = (ins & 0x3);
316318
CK((!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) ||
317319
(unsigned int)(n+256) < 512, RANGE_I);
318320
#endif
319321
b[pos++] = n;
320322
break;
321323
}
324+
case DASM_VREG:
325+
CK(n < 32, RANGE_VREG);
326+
b[pos++] = n;
327+
break;
322328
}
323329
}
324330
}
@@ -377,6 +383,7 @@ int dasm_link(Dst_DECL, size_t *szp)
377383
case DASM_IMM: case DASM_IMM6: case DASM_IMM12: case DASM_IMM13W:
378384
case DASM_IMML: pos++; break;
379385
case DASM_IMM13X: pos += 2; break;
386+
case DASM_VREG: pos++; break;
380387
}
381388
}
382389
stop: (void)0;
@@ -467,11 +474,14 @@ int dasm_encode(Dst_DECL, void *buffer)
467474
cp[-1] |= (dasm_imm13(n, *b++) << 10);
468475
break;
469476
case DASM_IMML: {
470-
int scale = (p[-2] >> 30);
477+
int scale = (ins & 0x3);
471478
cp[-1] |= (!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) ?
472479
((n << (10-scale)) | 0x01000000) : ((n & 511) << 12);
473480
break;
474481
}
482+
case DASM_VREG:
483+
cp[-1] |= (n & 0x1f) << (ins & 0x1f);
484+
break;
475485
default: *cp++ = ins; break;
476486
}
477487
}

ext/opcache/jit/dynasm/dasm_arm64.lua

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ local action_names = {
4040
"STOP", "SECTION", "ESC", "REL_EXT",
4141
"ALIGN", "REL_LG", "LABEL_LG",
4242
"REL_PC", "LABEL_PC", "IMM", "IMM6", "IMM12", "IMM13W", "IMM13X", "IMML",
43+
"VREG"
4344
}
4445

4546
-- Maximum number of section buffer positions for dasm_put().
@@ -246,7 +247,7 @@ local map_cond = {
246247

247248
local parse_reg_type
248249

249-
local function parse_reg(expr)
250+
local function parse_reg(expr, shift)
250251
if not expr then werror("expected register name") end
251252
local tname, ovreg = match(expr, "^([%w_]+):(@?%l%d+)$")
252253
local tp = map_type[tname or expr]
@@ -266,18 +267,29 @@ local function parse_reg(expr)
266267
elseif parse_reg_type ~= rt then
267268
werror("register size mismatch")
268269
end
269-
return r, tp
270+
return shl(r, shift or 0), tp
270271
end
271272
end
273+
-- Allow Rx(...) for dynamic register names
274+
local vrt, vreg = match(expr, "^R([xwqdshb])(%b())$")
275+
if vreg then
276+
if not parse_reg_type then
277+
parse_reg_type = vrt
278+
elseif parse_reg_type ~= vrt then
279+
werror("register size mismatch")
280+
end
281+
if shift then waction("VREG", shift, vreg) end
282+
return 0
283+
end
272284
werror("bad register name `"..expr.."'")
273285
end
274286

275287
local function parse_reg_base(expr)
276288
if expr == "sp" then return 0x3e0 end
277-
local base, tp = parse_reg(expr)
289+
local base, tp = parse_reg(expr, 5)
278290
if parse_reg_type ~= "x" then werror("bad register type") end
279291
parse_reg_type = false
280-
return shl(base, 5), tp
292+
return base, tp
281293
end
282294

283295
local parse_ctx = {}
@@ -403,7 +415,7 @@ local function parse_imm_load(imm, scale)
403415
end
404416
werror("out of range immediate `"..imm.."'")
405417
else
406-
waction("IMML", 0, imm)
418+
waction("IMML", scale, imm)
407419
return 0
408420
end
409421
end
@@ -470,7 +482,7 @@ local function parse_load(params, nparams, n, op)
470482
if reg and tailr ~= "" then
471483
local base, tp = parse_reg_base(reg)
472484
if tp then
473-
waction("IMML", 0, format(tp.ctypefmt, tailr))
485+
waction("IMML", shr(op, 30), format(tp.ctypefmt, tailr))
474486
return op + base
475487
end
476488
end
@@ -494,7 +506,7 @@ local function parse_load(params, nparams, n, op)
494506
op = op + parse_imm_load(imm, scale)
495507
else
496508
local p2b, p3b, p3s = match(p2a, "^,%s*([^,%s]*)%s*,?%s*(%S*)%s*(.*)$")
497-
op = op + shl(parse_reg(p2b), 16) + 0x00200800
509+
op = op + parse_reg(p2b, 16) + 0x00200800
498510
if parse_reg_type ~= "x" and parse_reg_type ~= "w" then
499511
werror("bad index register type")
500512
end
@@ -891,15 +903,15 @@ local function parse_template(params, template, nparams, pos)
891903
for p in gmatch(sub(template, 9), ".") do
892904
local q = params[n]
893905
if p == "D" then
894-
op = op + parse_reg(q); n = n + 1
906+
op = op + parse_reg(q, 0); n = n + 1
895907
elseif p == "N" then
896-
op = op + shl(parse_reg(q), 5); n = n + 1
908+
op = op + parse_reg(q, 5); n = n + 1
897909
elseif p == "M" then
898-
op = op + shl(parse_reg(q), 16); n = n + 1
910+
op = op + parse_reg(q, 16); n = n + 1
899911
elseif p == "A" then
900-
op = op + shl(parse_reg(q), 10); n = n + 1
912+
op = op + parse_reg(q, 10); n = n + 1
901913
elseif p == "m" then
902-
op = op + shl(parse_reg(params[n-1]), 16)
914+
op = op + parse_reg(params[n-1], 16)
903915

904916
elseif p == "p" then
905917
if q == "sp" then params[n] = "@x31" end

0 commit comments

Comments
 (0)