/*************************************************************//* * ARM64 (AArch64) assembler for TCC * * Based on ARM64 Architecture Reference Manual * Supports AArch64 assembler parsing and extended inline asm * with operands, constraints, and clobbers. */ #ifdef TARGET_DEFS_ONLY #define CONFIG_TCC_ASM/* 32 general purpose + 32 SIMD/FP registers */#define NB_ASM_REGS 64 ST_FUNC void g(int c);ST_FUNC void gen_le16(int c);ST_FUNC void gen_le32(int c); /*************************************************************/#else/*************************************************************/ #define USING_GLOBALS#include "tcc.h" /* Register type flags */#define REG_X 0x01 /* 64-bit general purpose */#define REG_W 0x02 /* 32-bit general purpose */#define REG_V 0x04 /* 128-bit SIMD */#define REG_D 0x08 /* 64-bit FP */#define REG_S 0x10 /* 32-bit FP */#define REG_H 0x20 /* 16-bit FP */#define REG_B 0x40 /* 8-bit SIMD */ /* Operand types */enum { OPT_REG, OPT_IM, OPT_ADDR, OPT_COND,}; #define OP_REG (1 << OPT_REG)#define OP_IM (1 << OPT_IM)#define OP_ADDR (1 << OPT_ADDR)#define OP_COND (1 << OPT_COND) /* Register allocation masks */#define REG_OUT_MASK 0x01#define REG_IN_MASK 0x02 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) /* ARM64 register constants */#define TREG_X0 0#define TREG_X1 1#define TREG_X2 2#define TREG_X3 3#define TREG_X4 4#define TREG_X5 5#define TREG_X6 6#define TREG_X7 7#define TREG_X8 8#define TREG_X9 9#define TREG_X10 10#define TREG_X11 11#define TREG_X12 12#define TREG_X13 13#define TREG_X14 14#define TREG_X15 15#define TREG_X16 16#define TREG_X17 17#define TREG_X18 18#define TREG_X19 19#define TREG_X20 20#define TREG_X21 21#define TREG_X22 22#define TREG_X23 23#define TREG_X24 24#define TREG_X25 25#define TREG_X26 26#define TREG_X27 27#define TREG_X28 28#define TREG_X29 29#define TREG_X30 30#define TREG_SP 31 #define ARM64_FREG_BASE 20#define ARM64_FREG_LAST (ARM64_FREG_BASE + 7) typedef struct Operand { uint32_t type; int8_t reg; int8_t reg2; uint8_t reg_type; uint8_t shift; uint8_t addr_mode; int reg_tok; ExprValue e;} Operand; enum { ADDR_OFF, ADDR_PRE, ADDR_POST,}; /* Forward declaration */static void parse_addr_operand(TCCState *s1, Operand *op); /* XXX: make it faster ? */ST_FUNC void g(int c){ int ind1; if (nocode_wanted) return; ind1 = ind + 1; if (ind1 > cur_text_section->data_allocated) section_realloc(cur_text_section, ind1); cur_text_section->data[ind] = c; ind = ind1;} ST_FUNC void gen_le16(int c){ g(c); g(c >> 8);} ST_FUNC void gen_le32(int c){ gen_le16(c); gen_le16(c >> 16);} ST_FUNC void gen_expr32(ExprValue *pe){ gen_le32(pe->v);} /* Emit 32-bit instruction */static void emit_instr32(uint32_t val){ if (nocode_wanted) return; if (ind + 4 > cur_text_section->data_allocated) section_realloc(cur_text_section, ind + 4); write32le(cur_text_section->data + ind, val); ind += 4;} /* Parse ARM64 register token for assembler syntax. */static int arm64_parse_asm_reg(int t){ /* X registers (64-bit) */ if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) return t - TOK_ASM_x0; /* W registers (32-bit) */ if (t >= TOK_ASM_w0 && t <= TOK_ASM_w30) return t - TOK_ASM_w0; /* V registers (128-bit SIMD) */ if (t >= TOK_ASM_v0 && t <= TOK_ASM_v31) return (t - TOK_ASM_v0) + 32; /* D registers (64-bit FP) */ if (t >= TOK_ASM_d0 && t <= TOK_ASM_d31) return (t - TOK_ASM_d0) + 32; /* S registers (32-bit FP) */ if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31) return (t - TOK_ASM_s0) + 32; /* H registers (16-bit FP) */ if (t >= TOK_ASM_h0 && t <= TOK_ASM_h31) return (t - TOK_ASM_h0) + 32; /* B registers (8-bit SIMD) */ if (t >= TOK_ASM_b0 && t <= TOK_ASM_b31) return (t - TOK_ASM_b0) + 32; /* Special registers */ if (t == TOK_ASM_sp || t == TOK_ASM_xzr || t == TOK_ASM_wzr) return 31; /* SP/ZR encoded as 31 */ return -1;} /* Get register type from token */static uint8_t get_reg_type(int t){ if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) return REG_X; if (t >= TOK_ASM_w0 && t <= TOK_ASM_w30) return REG_W; if (t >= TOK_ASM_v0 && t <= TOK_ASM_v31) return REG_V; if (t >= TOK_ASM_d0 && t <= TOK_ASM_d31) return REG_D; if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31) return REG_S; if (t >= TOK_ASM_h0 && t <= TOK_ASM_h31) return REG_H; if (t >= TOK_ASM_b0 && t <= TOK_ASM_b31) return REG_B; /* Special registers - sp is 64-bit, xzr is 64-bit, wzr is 32-bit */ if (t == TOK_ASM_sp || t == TOK_ASM_xzr) return REG_X; if (t == TOK_ASM_wzr) return REG_W; return REG_X;} /* Parse condition code */static int parse_condition(int t){ switch (t) { case TOK_ASM_eq: return 0; case TOK_ASM_ne: return 1; case TOK_ASM_cs: case TOK_ASM_hs: return 2; case TOK_ASM_cc: case TOK_ASM_lo: return 3; case TOK_ASM_mi: return 4; case TOK_ASM_pl: return 5; case TOK_ASM_vs: return 6; case TOK_ASM_vc: return 7; case TOK_ASM_hi: return 8; case TOK_ASM_ls: return 9; case TOK_ASM_ge: return 10; case TOK_ASM_lt: return 11; case TOK_ASM_gt: return 12; case TOK_ASM_le: return 13; case TOK_ASM_al: return 14; default: return -1; }} static int parse_barrier_option_name(int t){ const char *name; if (t < TOK_IDENT) return -1; name = get_tok_str(t, NULL); if (!strcmp(name, "oshld")) return 0x1; if (!strcmp(name, "oshst")) return 0x2; if (!strcmp(name, "osh")) return 0x3; if (!strcmp(name, "nshld")) return 0x5; if (!strcmp(name, "nshst")) return 0x6; if (!strcmp(name, "nsh")) return 0x7; if (!strcmp(name, "ishld")) return 0x9; if (!strcmp(name, "ishst")) return 0xA; if (!strcmp(name, "ish")) return 0xB; if (!strcmp(name, "ld")) return 0xD; if (!strcmp(name, "st")) return 0xE; if (!strcmp(name, "sy")) return 0xF; return -1;} static int arm64_parse_regvar(int t){ if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) return t - TOK_ASM_x0; if (t >= TOK_ASM_v0 && t <= TOK_ASM_v7) return ARM64_FREG_BASE + (t - TOK_ASM_v0); if (t >= TOK_ASM_d0 && t <= TOK_ASM_d7) return ARM64_FREG_BASE + (t - TOK_ASM_d0); if (t >= TOK_ASM_s0 && t <= TOK_ASM_s7) return ARM64_FREG_BASE + (t - TOK_ASM_s0); if (t >= TOK_ASM_h0 && t <= TOK_ASM_h7) return ARM64_FREG_BASE + (t - TOK_ASM_h0); if (t >= TOK_ASM_b0 && t <= TOK_ASM_b7) return ARM64_FREG_BASE + (t - TOK_ASM_b0); return -1;} /* Parse a single operand */static void parse_operand(TCCState *s1, Operand *op){ int reg; op->type = 0; op->reg = -1; op->reg2 = -1; op->reg_type = 0; op->shift = 0; op->addr_mode = ADDR_OFF; op->reg_tok = 0; /* Address operand in brackets [xn, ...] */ if (tok == '[') { parse_addr_operand(s1, op); return; } /* Register */ reg = arm64_parse_asm_reg(tok); if (reg >= 0) { op->type = OP_REG; op->reg = reg; op->reg_type = get_reg_type(tok); op->reg_tok = tok; next(); return; } /* Condition code */ reg = parse_condition(tok); if (reg >= 0) { op->type = OP_COND; op->reg = reg; next(); return; } /* Immediate or address expression */ if (tok == '#' || tok == ':' || tok == '@' || tok == '$') { next(); asm_expr(s1, &op->e); op->type = OP_IM; } else if (tok >= TOK_IDENT) { tcc_error("invalid operand '%s'", get_tok_str(tok, &tokc)); op->type = OP_IM; } else { asm_expr(s1, &op->e); op->type = OP_IM; }} /* Parse a symbolic/immediate expression operand used by branch instructions. */static void parse_expr_operand(TCCState *s1, Operand *op){ op->type = OP_IM; op->reg = -1; op->reg2 = -1; op->reg_type = 0; op->shift = 0; op->addr_mode = ADDR_OFF; op->reg_tok = 0; if (tok == '#' || tok == ':' || tok == '@' || tok == '$') next(); asm_expr(s1, &op->e);} /* Parse address operand in brackets [xn, ...] */static void parse_addr_operand(TCCState *s1, Operand *op){ int reg; op->type = OP_ADDR; op->reg = -1; op->reg2 = -1; op->e.v = 0; op->e.sym = NULL; op->addr_mode = ADDR_OFF; op->reg_tok = 0; skip('['); reg = arm64_parse_asm_reg(tok); if (reg < 0 || reg >= 32) { tcc_error("invalid register in address operand"); return; } op->reg = reg; op->reg_tok = tok; next(); /* Check for offset */ if (tok == ',') { next(); if (tok == '#' || tok == '@' || tok == '$') next(); asm_expr(s1, &op->e); } skip(']'); if (tok == '!') { op->addr_mode = ADDR_PRE; next(); } else if (tok == ',') { op->addr_mode = ADDR_POST; next(); if (tok == '#' || tok == '@' || tok == '$') next(); asm_expr(s1, &op->e); }} /* Generate MOVZ/MOVN/MOVK with base opcode */static void gen_mov_with_base(int rd, uint16_t imm, int shift, int is_64bit, uint32_t base_opcode){ uint32_t instr = base_opcode; if (is_64bit) instr |= ARM64_SF(1); /* shift is halfword index (0-3), encode as LSL #0/16/32/48 */ instr |= ARM64_IMM_HW(imm, shift); instr |= ARM64_RD(rd); emit_instr32(instr);} static void gen_movz(int rd, uint16_t imm, int shift, int is_64bit){ gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVZ);} /* Generate MOVN instruction */static void gen_movn(int rd, uint16_t imm, int shift, int is_64bit){ gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVN);} /* Generate MOVK instruction */static void gen_movk(int rd, uint16_t imm, int shift, int is_64bit){ gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVK);} /* Generate ADD (immediate) */static void gen_add_imm(int rd, int rn, uint32_t imm, int is_64bit, int setflags){ uint32_t instr = ARM64_ADD_IMM; uint32_t imm12; if (is_64bit) instr |= ARM64_SF(1); if (setflags) instr |= ARM64_S(1); if (imm <= 0xFFF) { imm12 = imm; } else if (!(imm & 0xFFF) && (imm >> 12) <= 0xFFF) { instr |= ARM64_SH(1); imm12 = imm >> 12; } else { tcc_error("add immediate out of range"); return; } instr |= ARM64_IMM12(imm12); instr |= ARM64_RN(rn); instr |= ARM64_RD(rd); emit_instr32(instr);} /* Generate SUB (immediate) */static void gen_sub_imm(int rd, int rn, uint32_t imm, int is_64bit, int setflags){ uint32_t instr = ARM64_SUB_IMM; uint32_t imm12; if (is_64bit) instr |= ARM64_SF(1); if (setflags) instr |= ARM64_S(1); if (imm <= 0xFFF) { imm12 = imm; } else if (!(imm & 0xFFF) && (imm >> 12) <= 0xFFF) { instr |= ARM64_SH(1); imm12 = imm >> 12; } else { tcc_error("sub immediate out of range"); return; } instr |= ARM64_IMM12(imm12); instr |= ARM64_RN(rn); instr |= ARM64_RD(rd); emit_instr32(instr);} /* Generate data processing register instruction */static void gen_dp_reg(uint32_t opcode, int rd, int rn, int rm, int is_64bit){ uint32_t instr = opcode; if (is_64bit) instr |= ARM64_SF(1); instr |= ARM64_RM(rm); instr |= ARM64_RN(rn); instr |= ARM64_RD(rd); emit_instr32(instr);} /* Generate LDR/STR (unsigned immediate) */static void gen_ldst_imm(uint32_t base_opcode, int rt, int rn, int32_t offset, int size_log2){ uint32_t instr = base_opcode; uint32_t unscaled_opcode = 0; uint32_t imm12; if (offset >= 0 && !(offset & ((1 << size_log2) - 1))) { imm12 = offset >> size_log2; if (imm12 <= 0xFFF) { instr |= ARM64_IMM12(imm12); instr |= ARM64_RN(rn); instr |= ARM64_RT(rt); emit_instr32(instr); return; } } switch (base_opcode) { case ARM64_LDR_X: unscaled_opcode = ARM64_LDUR_X; break; case ARM64_LDR_W: unscaled_opcode = ARM64_LDUR_W; break; case ARM64_LDR_B: unscaled_opcode = ARM64_LDUR_B; break; case ARM64_LDR_H: unscaled_opcode = ARM64_LDUR_H; break; case ARM64_LDR_D: unscaled_opcode = ARM64_LDUR_D_SIMD; break; case ARM64_STR_X: unscaled_opcode = ARM64_STUR_X; break; case ARM64_STR_W: unscaled_opcode = ARM64_STUR_W; break; case ARM64_STR_B: unscaled_opcode = ARM64_STUR_B; break;Lines 1–500 of 2276. Load more
/*************************************************************/ /* * ARM64 (AArch64) assembler for TCC * * Based on ARM64 Architecture Reference Manual * Supports AArch64 assembler parsing and extended inline asm * with operands, constraints, and clobbers. */ #ifdef TARGET_DEFS_ONLY #define CONFIG_TCC_ASM /* 32 general purpose + 32 SIMD/FP registers */ #define NB_ASM_REGS 64 ST_FUNC void g(int c); ST_FUNC void gen_le16(int c); ST_FUNC void gen_le32(int c); /*************************************************************/ #else /*************************************************************/ #define USING_GLOBALS #include "tcc.h" /* Register type flags */ #define REG_X 0x01 /* 64-bit general purpose */ #define REG_W 0x02 /* 32-bit general purpose */ #define REG_V 0x04 /* 128-bit SIMD */ #define REG_D 0x08 /* 64-bit FP */ #define REG_S 0x10 /* 32-bit FP */ #define REG_H 0x20 /* 16-bit FP */ #define REG_B 0x40 /* 8-bit SIMD */ /* Operand types */ enum { OPT_REG, OPT_IM, OPT_ADDR, OPT_COND, }; #define OP_REG (1 << OPT_REG) #define OP_IM (1 << OPT_IM) #define OP_ADDR (1 << OPT_ADDR) #define OP_COND (1 << OPT_COND) /* Register allocation masks */ #define REG_OUT_MASK 0x01 #define REG_IN_MASK 0x02 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) /* ARM64 register constants */ #define TREG_X0 0 #define TREG_X1 1 #define TREG_X2 2 #define TREG_X3 3 #define TREG_X4 4 #define TREG_X5 5 #define TREG_X6 6 #define TREG_X7 7 #define TREG_X8 8 #define TREG_X9 9 #define TREG_X10 10 #define TREG_X11 11 #define TREG_X12 12 #define TREG_X13 13 #define TREG_X14 14 #define TREG_X15 15 #define TREG_X16 16 #define TREG_X17 17 #define TREG_X18 18 #define TREG_X19 19 #define TREG_X20 20 #define TREG_X21 21 #define TREG_X22 22 #define TREG_X23 23 #define TREG_X24 24 #define TREG_X25 25 #define TREG_X26 26 #define TREG_X27 27 #define TREG_X28 28 #define TREG_X29 29 #define TREG_X30 30 #define TREG_SP 31 #define ARM64_FREG_BASE 20 #define ARM64_FREG_LAST (ARM64_FREG_BASE + 7) typedef struct Operand { uint32_t type; int8_t reg; int8_t reg2; uint8_t reg_type; uint8_t shift; uint8_t addr_mode; int reg_tok; ExprValue e; } Operand; enum { ADDR_OFF, ADDR_PRE, ADDR_POST, }; /* Forward declaration */ static void parse_addr_operand(TCCState *s1, Operand *op); /* XXX: make it faster ? */ ST_FUNC void g(int c) { int ind1; if (nocode_wanted) return; ind1 = ind + 1; if (ind1 > cur_text_section->data_allocated) section_realloc(cur_text_section, ind1); cur_text_section->data[ind] = c; ind = ind1; } ST_FUNC void gen_le16(int c) { g(c); g(c >> 8); } ST_FUNC void gen_le32(int c) { gen_le16(c); gen_le16(c >> 16); } ST_FUNC void gen_expr32(ExprValue *pe) { gen_le32(pe->v); } /* Emit 32-bit instruction */ static void emit_instr32(uint32_t val) { if (nocode_wanted) return; if (ind + 4 > cur_text_section->data_allocated) section_realloc(cur_text_section, ind + 4); write32le(cur_text_section->data + ind, val); ind += 4; } /* Parse ARM64 register token for assembler syntax. */ static int arm64_parse_asm_reg(int t) { /* X registers (64-bit) */ if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) return t - TOK_ASM_x0; /* W registers (32-bit) */ if (t >= TOK_ASM_w0 && t <= TOK_ASM_w30) return t - TOK_ASM_w0; /* V registers (128-bit SIMD) */ if (t >= TOK_ASM_v0 && t <= TOK_ASM_v31) return (t - TOK_ASM_v0) + 32; /* D registers (64-bit FP) */ if (t >= TOK_ASM_d0 && t <= TOK_ASM_d31) return (t - TOK_ASM_d0) + 32; /* S registers (32-bit FP) */ if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31) return (t - TOK_ASM_s0) + 32; /* H registers (16-bit FP) */ if (t >= TOK_ASM_h0 && t <= TOK_ASM_h31) return (t - TOK_ASM_h0) + 32; /* B registers (8-bit SIMD) */ if (t >= TOK_ASM_b0 && t <= TOK_ASM_b31) return (t - TOK_ASM_b0) + 32; /* Special registers */ if (t == TOK_ASM_sp || t == TOK_ASM_xzr || t == TOK_ASM_wzr) return 31; /* SP/ZR encoded as 31 */ return -1; } /* Get register type from token */ static uint8_t get_reg_type(int t) { if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) return REG_X; if (t >= TOK_ASM_w0 && t <= TOK_ASM_w30) return REG_W; if (t >= TOK_ASM_v0 && t <= TOK_ASM_v31) return REG_V; if (t >= TOK_ASM_d0 && t <= TOK_ASM_d31) return REG_D; if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31) return REG_S; if (t >= TOK_ASM_h0 && t <= TOK_ASM_h31) return REG_H; if (t >= TOK_ASM_b0 && t <= TOK_ASM_b31) return REG_B; /* Special registers - sp is 64-bit, xzr is 64-bit, wzr is 32-bit */ if (t == TOK_ASM_sp || t == TOK_ASM_xzr) return REG_X; if (t == TOK_ASM_wzr) return REG_W; return REG_X; } /* Parse condition code */ static int parse_condition(int t) { switch (t) { case TOK_ASM_eq: return 0; case TOK_ASM_ne: return 1; case TOK_ASM_cs: case TOK_ASM_hs: return 2; case TOK_ASM_cc: case TOK_ASM_lo: return 3; case TOK_ASM_mi: return 4; case TOK_ASM_pl: return 5; case TOK_ASM_vs: return 6; case TOK_ASM_vc: return 7; case TOK_ASM_hi: return 8; case TOK_ASM_ls: return 9; case TOK_ASM_ge: return 10; case TOK_ASM_lt: return 11; case TOK_ASM_gt: return 12; case TOK_ASM_le: return 13; case TOK_ASM_al: return 14; default: return -1; } } static int parse_barrier_option_name(int t) { const char *name; if (t < TOK_IDENT) return -1; name = get_tok_str(t, NULL); if (!strcmp(name, "oshld")) return 0x1; if (!strcmp(name, "oshst")) return 0x2; if (!strcmp(name, "osh")) return 0x3; if (!strcmp(name, "nshld")) return 0x5; if (!strcmp(name, "nshst")) return 0x6; if (!strcmp(name, "nsh")) return 0x7; if (!strcmp(name, "ishld")) return 0x9; if (!strcmp(name, "ishst")) return 0xA; if (!strcmp(name, "ish")) return 0xB; if (!strcmp(name, "ld")) return 0xD; if (!strcmp(name, "st")) return 0xE; if (!strcmp(name, "sy")) return 0xF; return -1; } static int arm64_parse_regvar(int t) { if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) return t - TOK_ASM_x0; if (t >= TOK_ASM_v0 && t <= TOK_ASM_v7) return ARM64_FREG_BASE + (t - TOK_ASM_v0); if (t >= TOK_ASM_d0 && t <= TOK_ASM_d7) return ARM64_FREG_BASE + (t - TOK_ASM_d0); if (t >= TOK_ASM_s0 && t <= TOK_ASM_s7) return ARM64_FREG_BASE + (t - TOK_ASM_s0); if (t >= TOK_ASM_h0 && t <= TOK_ASM_h7) return ARM64_FREG_BASE + (t - TOK_ASM_h0); if (t >= TOK_ASM_b0 && t <= TOK_ASM_b7) return ARM64_FREG_BASE + (t - TOK_ASM_b0); return -1; } /* Parse a single operand */ static void parse_operand(TCCState *s1, Operand *op) { int reg; op->type = 0; op->reg = -1; op->reg2 = -1; op->reg_type = 0; op->shift = 0; op->addr_mode = ADDR_OFF; op->reg_tok = 0; /* Address operand in brackets [xn, ...] */ if (tok == '[') { parse_addr_operand(s1, op); return; } /* Register */ reg = arm64_parse_asm_reg(tok); if (reg >= 0) { op->type = OP_REG; op->reg = reg; op->reg_type = get_reg_type(tok); op->reg_tok = tok; next(); return; } /* Condition code */ reg = parse_condition(tok); if (reg >= 0) { op->type = OP_COND; op->reg = reg; next(); return; } /* Immediate or address expression */ if (tok == '#' || tok == ':' || tok == '@' || tok == '$') { next(); asm_expr(s1, &op->e); op->type = OP_IM; } else if (tok >= TOK_IDENT) { tcc_error("invalid operand '%s'", get_tok_str(tok, &tokc)); op->type = OP_IM; } else { asm_expr(s1, &op->e); op->type = OP_IM; } } /* Parse a symbolic/immediate expression operand used by branch instructions. */ static void parse_expr_operand(TCCState *s1, Operand *op) { op->type = OP_IM; op->reg = -1; op->reg2 = -1; op->reg_type = 0; op->shift = 0; op->addr_mode = ADDR_OFF; op->reg_tok = 0; if (tok == '#' || tok == ':' || tok == '@' || tok == '$') next(); asm_expr(s1, &op->e); } /* Parse address operand in brackets [xn, ...] */ static void parse_addr_operand(TCCState *s1, Operand *op) { int reg; op->type = OP_ADDR; op->reg = -1; op->reg2 = -1; op->e.v = 0; op->e.sym = NULL; op->addr_mode = ADDR_OFF; op->reg_tok = 0; skip('['); reg = arm64_parse_asm_reg(tok); if (reg < 0 || reg >= 32) { tcc_error("invalid register in address operand"); return; } op->reg = reg; op->reg_tok = tok; next(); /* Check for offset */ if (tok == ',') { next(); if (tok == '#' || tok == '@' || tok == '$') next(); asm_expr(s1, &op->e); } skip(']'); if (tok == '!') { op->addr_mode = ADDR_PRE; next(); } else if (tok == ',') { op->addr_mode = ADDR_POST; next(); if (tok == '#' || tok == '@' || tok == '$') next(); asm_expr(s1, &op->e); } } /* Generate MOVZ/MOVN/MOVK with base opcode */ static void gen_mov_with_base(int rd, uint16_t imm, int shift, int is_64bit, uint32_t base_opcode) { uint32_t instr = base_opcode; if (is_64bit) instr |= ARM64_SF(1); /* shift is halfword index (0-3), encode as LSL #0/16/32/48 */ instr |= ARM64_IMM_HW(imm, shift); instr |= ARM64_RD(rd); emit_instr32(instr); } static void gen_movz(int rd, uint16_t imm, int shift, int is_64bit) { gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVZ); } /* Generate MOVN instruction */ static void gen_movn(int rd, uint16_t imm, int shift, int is_64bit) { gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVN); } /* Generate MOVK instruction */ static void gen_movk(int rd, uint16_t imm, int shift, int is_64bit) { gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVK); } /* Generate ADD (immediate) */ static void gen_add_imm(int rd, int rn, uint32_t imm, int is_64bit, int setflags) { uint32_t instr = ARM64_ADD_IMM; uint32_t imm12; if (is_64bit) instr |= ARM64_SF(1); if (setflags) instr |= ARM64_S(1); if (imm <= 0xFFF) { imm12 = imm; } else if (!(imm & 0xFFF) && (imm >> 12) <= 0xFFF) { instr |= ARM64_SH(1); imm12 = imm >> 12; } else { tcc_error("add immediate out of range"); return; } instr |= ARM64_IMM12(imm12); instr |= ARM64_RN(rn); instr |= ARM64_RD(rd); emit_instr32(instr); } /* Generate SUB (immediate) */ static void gen_sub_imm(int rd, int rn, uint32_t imm, int is_64bit, int setflags) { uint32_t instr = ARM64_SUB_IMM; uint32_t imm12; if (is_64bit) instr |= ARM64_SF(1); if (setflags) instr |= ARM64_S(1); if (imm <= 0xFFF) { imm12 = imm; } else if (!(imm & 0xFFF) && (imm >> 12) <= 0xFFF) { instr |= ARM64_SH(1); imm12 = imm >> 12; } else { tcc_error("sub immediate out of range"); return; } instr |= ARM64_IMM12(imm12); instr |= ARM64_RN(rn); instr |= ARM64_RD(rd); emit_instr32(instr); } /* Generate data processing register instruction */ static void gen_dp_reg(uint32_t opcode, int rd, int rn, int rm, int is_64bit) { uint32_t instr = opcode; if (is_64bit) instr |= ARM64_SF(1); instr |= ARM64_RM(rm); instr |= ARM64_RN(rn); instr |= ARM64_RD(rd); emit_instr32(instr); } /* Generate LDR/STR (unsigned immediate) */ static void gen_ldst_imm(uint32_t base_opcode, int rt, int rn, int32_t offset, int size_log2) { uint32_t instr = base_opcode; uint32_t unscaled_opcode = 0; uint32_t imm12; if (offset >= 0 && !(offset & ((1 << size_log2) - 1))) { imm12 = offset >> size_log2; if (imm12 <= 0xFFF) { instr |= ARM64_IMM12(imm12); instr |= ARM64_RN(rn); instr |= ARM64_RT(rt); emit_instr32(instr); return; } } switch (base_opcode) { case ARM64_LDR_X: unscaled_opcode = ARM64_LDUR_X; break; case ARM64_LDR_W: unscaled_opcode = ARM64_LDUR_W; break; case ARM64_LDR_B: unscaled_opcode = ARM64_LDUR_B; break; case ARM64_LDR_H: unscaled_opcode = ARM64_LDUR_H; break; case ARM64_LDR_D: unscaled_opcode = ARM64_LDUR_D_SIMD; break; case ARM64_STR_X: unscaled_opcode = ARM64_STUR_X; break; case ARM64_STR_W: unscaled_opcode = ARM64_STUR_W; break; case ARM64_STR_B: unscaled_opcode = ARM64_STUR_B; break;