From f2285400e65dafe730a073b3ca92494d72c7295b Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 2 Mar 2026 17:52:57 +0100 Subject: add bswap16/32/64 - frontend: __builtin_bswapX intrinsics - backend: ObswapX instructions --- c/builtin.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++---------- c/c.c | 42 ++++++++++++++++++------------------- c/c.h | 1 + endian.h | 5 +++-- ir/builder.c | 2 ++ ir/fold.c | 4 ++++ ir/op.def | 3 +++ test/08-bit.c | 6 ++++++ x86_64/emit.c | 25 +++++++++++++++++++--- x86_64/isel.c | 8 ++++++++ 10 files changed, 125 insertions(+), 37 deletions(-) diff --git a/c/builtin.c b/c/builtin.c index aabd750..5c59857 100644 --- a/c/builtin.c +++ b/c/builtin.c @@ -32,13 +32,13 @@ callcheck(const struct span *span, int nparam, const union type *param, int narg return callcheck(&ex->span, countof(par)-1, par+1, ex->narg, ex->sub+1); \ } +/* __builtin_va_start */ static bool va_start_sema(struct comp *cm, struct expr *ex) { ex->ty = mktype(TYVOID); return callcheck(&ex->span, 1, &cvalistty, ex->narg, ex->sub+1); } - static union ref va_start_comp(struct function *fn, struct expr *ex, bool discard) { @@ -50,8 +50,7 @@ va_start_comp(struct function *fn, struct expr *ex, bool discard) return NOREF; } -DEF_FNLIKE_SEMA(trap, mktype(TYVOID), ) - +/* __builtin_va_end */ static bool va_end_sema(struct comp *cm, struct expr *ex) { @@ -65,13 +64,13 @@ va_end_comp(struct function *fn, struct expr *ex, bool discard) return NOREF; } +/* __builtin_va_copy */ DEF_FNLIKE_SEMA(va_copy, mktype(TYVOID), cvalistty, cvalistty) - static union ref va_copy_comp(struct function *fn, struct expr *ex, bool discard) { union irtype typ = mkirtype(cvalistty.t == TYARRAY ? typechild(cvalistty) : cvalistty); - for (int i = 1; i < 2; ++i) + for (int i = 1; i <= 2; ++i) assert(typedecay(ex->sub[i].ty).bits == typedecay(cvalistty).bits); union ref dst = compileexpr(fn, &ex->sub[1], 0), src = compileexpr(fn, &ex->sub[2], 0); addinstr(fn, mkarginstr(typ, dst)); @@ -80,6 +79,8 @@ va_copy_comp(struct function *fn, struct expr *ex, bool discard) return NOREF; } +/* __builtin_trap */ +DEF_FNLIKE_SEMA(trap, mktype(TYVOID), ) static union ref trap_comp(struct function *fn, struct expr *ex, bool discard) { @@ -88,17 +89,51 @@ trap_comp(struct function *fn, struct expr *ex, bool discard) return NOREF; } -union ref -builtin_va_arg_comp(struct function *fn, const struct expr *ex, bool discard) +static inline union ref +cvtintref(struct function *fn, enum irclass dst, union ref src) { - assert(ex->t == EVAARG && ex->ty.t); - enum irclass k = isagg(ex->ty) ? KPTR : type2cls[scalartypet(ex->ty)]; - return addinstr(fn, mkinstr(Ovaarg, k, compileexpr(fn, ex->sub, 0), mktyperef(mkirtype(ex->ty)))); + if (src.t == RTMP) { + if (insrescls(instrtab[src.i]) != dst) + return addinstr(fn, mkinstr(Ocopy, dst, src)); + return src; + } else if (isintcon(src)) { + vlong x = intconval(src); + return mkintcon(dst, cls2siz[dst] == 4 ? (int)x : x); + } + assert(!"int ref?"); +} + +/* __builtin_bswap16 */ +DEF_FNLIKE_SEMA(bswap16, mktype(TYUSHORT), mktype(TYUSHORT)) +static union ref +bswap16_comp(struct function *fn, struct expr *ex, bool discard) +{ + assert(isint(ex->ty)); + return irunop(fn, Obswap16, KI32, scalarcvt(fn, ex->ty, ex->sub[1].ty, + compileexpr(fn, &ex->sub[1], 0))); +} +/* __builtin_bswap32 */ +DEF_FNLIKE_SEMA(bswap32, mktype(TYUINT), mktype(TYUINT)) +static union ref +bswap32_comp(struct function *fn, struct expr *ex, bool discard) +{ + assert(isint(ex->ty)); + return irunop(fn, Obswap32, KI32, scalarcvt(fn, ex->ty, ex->sub[1].ty, + compileexpr(fn, &ex->sub[1], 0))); +} +/* __builtin_bswap64 */ +DEF_FNLIKE_SEMA(bswap64, mktype(TYUVLONG), mktype(TYUVLONG)) +static union ref +bswap64_comp(struct function *fn, struct expr *ex, bool discard) +{ + assert(isint(ex->ty)); + return irunop(fn, Obswap64, KI64, scalarcvt(fn, ex->ty, ex->sub[1].ty, + compileexpr(fn, &ex->sub[1], 0))); } #define LIST_BUILTINS(_) \ _(va_start) _(va_copy) _(va_end) \ - _(trap) + _(trap) _(bswap16) _(bswap32) _(bswap64) static const struct { const char *name; @@ -121,6 +156,15 @@ putbuiltins(struct env *env) } } +/* this is separate because it's a keyword */ +union ref +builtin_va_arg_comp(struct function *fn, const struct expr *ex, bool discard) +{ + assert(ex->t == EVAARG && ex->ty.t); + enum irclass k = isagg(ex->ty) ? KPTR : type2cls[scalartypet(ex->ty)]; + return addinstr(fn, mkinstr(Ovaarg, k, compileexpr(fn, ex->sub, 0), mktyperef(mkirtype(ex->ty)))); +} + bool hasbuiltin(const char *name, uint len) { diff --git a/c/c.c b/c/c.c index 2c78937..cd21e02 100644 --- a/c/c.c +++ b/c/c.c @@ -3146,8 +3146,8 @@ isboollike(struct function *fn, union ref r) return 0; } -static union ref -cvt(struct function *fn, union type to, union type from, union ref ref) +union ref +scalarcvt(struct function *fn, union type to, union type from, union ref ref) { enum irclass kto = type2cls[scalartypet(to)], kfrom = type2cls[scalartypet(from)]; enum op op; @@ -3172,7 +3172,7 @@ cvt(struct function *fn, union type to, union type from, union ref ref) if (to.t == TYBOOL) { if (from.t == TYBOOL) return ref; if (isboollike(fn, ref)) - return kfrom == KI32 ? ref : cvt(fn, mktype(TYINT), from, ref); + return kfrom == KI32 ? ref : scalarcvt(fn, mktype(TYINT), from, ref); return irbinop(fn, Oneq, kfrom, ref, ZEROREF); } else if (kfrom == KI32 && issigned(from)) op = Oexts32; @@ -3223,7 +3223,7 @@ genptroff(struct function *fn, enum op op, uint siz, union ref ptr, union ref off; assert(siz); - idx = cvt(fn, mktype(targ_sizetype), t, idx); + idx = scalarcvt(fn, mktype(targ_sizetype), t, idx); if (siz == 1) off = idx; else if (idx.t == RICON) { if (op == Osub) op = Oadd, idx.i = -idx.i; @@ -3340,7 +3340,7 @@ Recur: } else { union ref val = exprvalue(fn, ex); if (isscalar(phis->typ)) - val = cvt(fn, phis->typ, ex->ty, val); + val = scalarcvt(fn, phis->typ, ex->ty, val); else assert(ex->ty.bits == phis->typ.bits); vpush(&phis->ref, val); } @@ -3396,7 +3396,7 @@ compilecall(struct function *fn, const struct expr *ex) for (int i = 0; i < ex->narg; ++i) { struct expr *arg = &sub[i+1]; union type ty = i < td->nmemb ? td->param[i] : argpromote(arg->ty); - union ref r = cvt(fn, ty, typedecay(arg->ty), exprvalue(fn, arg)); + union ref r = scalarcvt(fn, ty, typedecay(arg->ty), exprvalue(fn, arg)); vpush(&insns, mkarginstr(mkirtype(ty), r)); } for (int i = 0; i < insns.n; ++i) @@ -3541,7 +3541,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) case EPLUS: r = compileexpr(fn, sub, discard); if (discard) return NOREF; - r = cvt(fn, ex->ty, sub->ty, r); + r = scalarcvt(fn, ex->ty, sub->ty, r); if (isint(ex->ty) && (typesize(ex->ty) < typesize(sub->ty) || issigned(ex->ty) != issigned(sub->ty))) return narrow(fn, type2cls[scalartypet(ex->ty)], ex->ty, r, 0); return r; @@ -3553,7 +3553,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) Unary: l = compileexpr(fn, sub, discard); if (discard) return NOREF; - l = cvt(fn, ex->ty, sub->ty, l); + l = scalarcvt(fn, ex->ty, sub->ty, l); return irunop(fn, op, cls, l); case ELOGNOT: for (; sub->t == ELOGNOT; ex = sub, sub = sub->sub) @@ -3561,7 +3561,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) op = Oequ + swp; l = compileexpr(fn, sub, discard); if (discard) return NOREF; - l = cvt(fn, ex->ty, sub->ty, l); + l = scalarcvt(fn, ex->ty, sub->ty, l); r = mkintcon(cls, 0); return irbinop(fn, op, cls, l, r); case EDEREF: @@ -3609,8 +3609,8 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) return genptrdiff(fn, typesize(typechild(sub[0].ty)), l, r); } else if ((op != Oadd && op != Osub) || cls != KPTR) { /* num OP num */ - l = cvt(fn, ex->ty, sub[0].ty, l); - r = cvt(fn, ex->ty, sub[1].ty, r); + l = scalarcvt(fn, ex->ty, sub[0].ty, l); + r = scalarcvt(fn, ex->ty, sub[1].ty, r); } else { assert(isptrcvt(sub[0].ty)); /* ptr +/- num */ @@ -3680,13 +3680,13 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) l = compileexpr(fn, &sub[0], discard); r = compileexpr(fn, &sub[1], discard); if (discard) return NOREF; - l = cvt(fn, ty, sub[0].ty, l); - r = cvt(fn, ty, sub[1].ty, r); + l = scalarcvt(fn, ty, sub[0].ty, l); + r = scalarcvt(fn, ty, sub[1].ty, r); cls = type2cls[ty.t]; return irbinop(fn, op, cls, l, r); case ESET: assert(isscalar(ex->ty)); - q = cvt(fn, sub[0].ty, sub[1].ty, exprvalue(fn, &sub[1])); + q = scalarcvt(fn, sub[0].ty, sub[1].ty, exprvalue(fn, &sub[1])); if (sub[0].t == EGETF && (bitsiz = sub[0].fld.bitsiz)) { /* bit-field */ adr = expraddr(fn, &sub[0].sub[0]); @@ -3744,10 +3744,10 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) adr = expraddr(fn, &sub[0]); l = genload(fn, ex->ty, adr, ex->qual & QVOLATILE); if ((op != Oadd && op != Osub) || cls != KPTR) { - l = cvt(fn, ty, sub[0].ty, l); - r = cvt(fn, ty, sub[1].ty, r); + l = scalarcvt(fn, ty, sub[0].ty, l); + r = scalarcvt(fn, ty, sub[1].ty, r); q = irbinop(fn, op, type2cls[ty.t], l, r); - q = cvt(fn, ex->ty, ty, q); + q = scalarcvt(fn, ex->ty, ty, q); } else { q = genptroff(fn, op, typesize(typechild(ex->ty)), l, sub[1].ty, r); } @@ -3764,7 +3764,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) for (bool c; knowntruthy(&c, &ex->sub[0]);) { r = compileexpr(fn, &ex->sub[2-c], discard); if (discard) return NOREF; - return cvt(fn, ex->ty, ex->sub[2-c].ty, r); + return scalarcvt(fn, ex->ty, ex->sub[2-c].ty, r); } if (ex->ty.t == TYVOID || discard) { @@ -3789,7 +3789,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) c ^= ex->t == ELOGIOR; r = compileexpr(fn, &ex->sub[c], discard); if (discard) return NOREF; - return cvt(fn, mktype(TYBOOL), ex->sub[c].ty, r); + return scalarcvt(fn, mktype(TYBOOL), ex->sub[c].ty, r); } return condexprvalue(fn, ex, discard); case ESEQ: @@ -4314,7 +4314,7 @@ stmt(struct comp *cm, struct function *fn) } EMITS { if (isscalar(fn->retty)) - r = cvt(fn, fn->retty, ex.ty, exprvalue(fn, &ex)); + r = scalarcvt(fn, fn->retty, ex.ty, exprvalue(fn, &ex)); else if (fn->retty.t == TYVOID) r = (expreffects(fn, &ex), NOREF); else @@ -4424,7 +4424,7 @@ localdecl(struct comp *cm, struct function *fn, bool forini) structcopy(fn, ty, mkref(RTMP, decl.id), exprvalue(fn, &ini)); else { genstore(fn, ty, mkref(RTMP, decl.id), - cvt(fn, ty, ini.ty, exprvalue(fn, &ini))); + scalarcvt(fn, ty, ini.ty, exprvalue(fn, &ini))); } } } else if (decl.scls == SCEXTERN) { diff --git a/c/c.h b/c/c.h index da30616..a5f702e 100644 --- a/c/c.h +++ b/c/c.h @@ -108,6 +108,7 @@ struct function; int envadddecl(struct env *env, const struct decl *d); bool assigncheck(union type t, const struct expr *src); union ref expraddr(struct function *, const struct expr *); +union ref scalarcvt(struct function *, union type to, union type from, union ref); union ref compileexpr(struct function *, const struct expr *, bool discard); /** builtin.c **/ diff --git a/endian.h b/endian.h index 08ce78f..34b7721 100644 --- a/endian.h +++ b/endian.h @@ -2,6 +2,7 @@ #define ENDIAN_H_ #include "common.h" +extern bool targ_bigendian; /*** Macros and functions for endian specific memory access ***/ @@ -121,7 +122,7 @@ rd32targ(uchar *p) { uint x; memcpy(&x, p, sizeof x); - if (!hostntarg_sameendian()) x = bswap16(x); + if (!hostntarg_sameendian()) x = bswap32(x); return x; } @@ -130,7 +131,7 @@ rd64targ(uchar *p) { uvlong x; memcpy(&x, p, sizeof x); - if (!hostntarg_sameendian()) x = bswap16(x); + if (!hostntarg_sameendian()) x = bswap64(x); return x; } diff --git a/ir/builder.c b/ir/builder.c index 6758533..206e66d 100644 --- a/ir/builder.c +++ b/ir/builder.c @@ -191,6 +191,8 @@ irunop(struct function *fn, enum op op, enum irclass k, union ref a) case Oexts32: case Oextu32: case Ocopy: break; + case Obswap16: case Obswap32: case Obswap64: + break; default: assert(!"unop?"); } return fn ? addinstr(fn, mkinstr(op, k, a)) : NOREF; diff --git a/ir/fold.c b/ir/fold.c index c87ca4d..ba50e40 100644 --- a/ir/fold.c +++ b/ir/fold.c @@ -1,4 +1,5 @@ #include "ir.h" +#include "../endian.h" static union ref foldint(enum op op, enum irclass k, union ref lr, union ref rr) @@ -24,6 +25,9 @@ foldint(enum op op, enum irclass k, union ref lr, union ref rr) case Oextu16: x = (ushort)l.s; break; case Oexts32: x = (int)l.s; break; case Oextu32: x = (uint)l.s; break; + case Obswap16: x = bswap16(l.u); break; + case Obswap32: x = bswap32(l.u); break; + case Obswap64: x = bswap64(l.u); break; case Oadd: x = l.u + r.u; break; case Osub: x = l.u - r.u; break; case Omul: x = l.u * r.u; break; diff --git a/ir/op.def b/ir/op.def index ac753c0..4a18b4b 100644 --- a/ir/op.def +++ b/ir/op.def @@ -20,6 +20,9 @@ _(exts16, 1) _(extu16, 1) _(exts32, 1) _(extu32, 1) +_(bswap16, 1) +_(bswap32, 1) +_(bswap64, 1) _(add, 2) _(sub, 2) _(mul, 2) diff --git a/test/08-bit.c b/test/08-bit.c index 5854cad..1a157e2 100644 --- a/test/08-bit.c +++ b/test/08-bit.c @@ -1,6 +1,7 @@ /* EXPECT: expect 6, -4, 7, -1 6, -4, 7, -1 +CEFA CDAB3412 AA000000000000FF */ int xor(int a) { @@ -37,6 +38,10 @@ struct s2 bitfcopy2(struct s2 x) { return (struct s2){x.a}; } +int bswap16(int x) { return __builtin_bswap16(x); } +int bswap32(int x) { return __builtin_bswap32(x); } +long long bswap64(long long x) { return __builtin_bswap64(x); } + int main(int p) { extern int printf(const char *, ...); @@ -50,6 +55,7 @@ int main(int p) { foo.a = xor(foo.a |= 3); printf("expect %d, -4, 7, %d\n", 5+p, (short)((-1|3)^3|555)); printf(" %d, %d, %lld, %d\n", foo.x, foo.k+=1, foo.h, foo.a); + printf("%X %X %llX\n", bswap16(0xFACE), bswap32(0x1234ABCD), bswap64(0xFF000000000000AAll)); int x = 42, *a = &x, diff --git a/x86_64/emit.c b/x86_64/emit.c index 5dbbed1..f951689 100644 --- a/x86_64/emit.c +++ b/x86_64/emit.c @@ -201,6 +201,7 @@ enum operenc { EN_MI8, /* mem, imm8 with /x */ EN_MI16, /* mem, imm16 with /x */ EN_MI32, /* mem, imm32 with /x */ + EN_O, /* reg with op + reg */ EN_OI, /* reg, imm32 with op + reg */ EN_I8, /* imm8 */ EN_I32, /* imm32 */ @@ -409,12 +410,12 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o else if (en->operenc == EN_RI8) B(src.imm); break; - case EN_OI: + case EN_O: case EN_OI: rex |= (dst.reg >> 3) << 0; /* REX.B */ if (rex) B(0x40 | rex); - B(*opc++ + (dst.reg & 7)); D(opc, nopc - 1); - I32(src.imm); + B(opc[nopc-1] + (dst.reg & 7)); + if (en->operenc == EN_OI) I32(src.imm); break; case EN_I8: if (rex) B(0x40 | rex); @@ -607,6 +608,9 @@ DEFINSTR2(Xsar, {4|8, PGPR, PI32, O("\xC1"), EN_RI8, .ext=7}, /* SAR r32/64, imm */ {4|8, PGPR, PRCX, O("\xD3"), EN_R, .ext=7}, /* SAR r32/64, CL */ ) +DEFINSTR2(Xrolw, + {-1, PGPR, PI8, O("\x66\xC1"), EN_RI8}, /* ROL r16, imm */ +) DEFINSTR2(Xshr, {4|8, PGPR, P1, O("\xD1"), EN_R, .ext=5}, /* SHR r32/64, 1 */ {4|8, PGPR, PI32, O("\xC1"), EN_RI8, .ext=5}, /* SHR r32/64, imm */ @@ -650,6 +654,9 @@ DEFINSTR1(Xdiv, {4|8, PGPR, 0, O("\xF7"), EN_R, .ext=6}, /* DIV r32/64 */ {4|8, PMEM, 0, O("\xF7"), EN_M, .ext=6}, /* DIV m32/64 */ ) +DEFINSTR1(Xbswap, + {4|8, PGPR, 0, O("\x0F\xC8"), EN_O}, /* BSWAP r32/64 */ +) DEFINSTR1(Xcall, {-1, PSYM, 0, O("\xE8"), EN_R32, .norexw=1}, /* CALL rel32 */ {-1, PGPR, 0, O("\xFF"), EN_R, .ext=2, .norexw=1}, /* CALL r64 */ @@ -1084,6 +1091,18 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int curi, struc assert(ins->reg-1 == dst.reg); X1(pcode, cls, dst); break; + case Obswap16: + dst = mkregoper(ins->l); + assert(ins->reg-1 == dst.reg); + if (dst.reg < 4) { /* AX,BX,CX,DX */ + /* XCHG rH, rL */ + B(0x86), B(0xC4 | dst.reg | (dst.reg)<<3); + } else { + /* ROL r16,8 */ + Xrolw(pcode, KI32, dst, mkoper(OIMM, .imm = 8)); + } + break; + case Obswap32: case Obswap64: X1 = Xbswap; goto ALU1; case Omul: if (kisint(cls)) Ximul(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), ref2oper(ins->r)); diff --git a/x86_64/isel.c b/x86_64/isel.c index fffc4c9..19e8d0c 100644 --- a/x86_64/isel.c +++ b/x86_64/isel.c @@ -560,6 +560,14 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) case Ocopy: fixarg(&ins->l, ins, blk, curi); break; + case Obswap16: case Obswap32: case Obswap64: + ins->inplace = 1; + if (ins->l.t != RTMP) { + ins->l = insertinstr(blk, *curi, mkinstr(Ocopy, ins->cls, ins->l)); + fixarg(&instrtab[ins->l.i].l, ins, blk, curi); + ++*curi; + } + break; case Oxvaprologue: fuseaddr(&ins->l, blk, curi); assert(ins->l.t == RADDR); -- cgit v1.2.3