2276 lines · 62.1 KB · C
RawDownload
/*************************************************************//* *  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.