Skip to content

Instantly share code, notes, and snippets.

@tommythorn
Created April 4, 2020 22:30
Show Gist options
  • Save tommythorn/a45433c01e20c2a01d6d5fa8d2acfb80 to your computer and use it in GitHub Desktop.
Save tommythorn/a45433c01e20c2a01d6d5fa8d2acfb80 to your computer and use it in GitHub Desktop.
RVC Decoder aka. GCC destroyer
// RVC decoder, Copyright (C) 2020 Tommy Thorn
/* This file is based on RVC.scala, licensed under Apache License Version 2.0 */
// *** WARNING ***
// This is work-in-progress
// Released just to show how it totally destroys GCC (~ 2 lines/s and
// 6 GiB working set)
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
uint32_t
rvcdecoder(uint32_t insn, int xlen)
{
#define _sz(f) (0 ? f)
#define _da(f) (1 ? f)
#define pack(da,sz) da : sz
#define x(hi,lo) ((insn >> lo) & ((1 << (hi-lo+1))-1)) : hi-lo+1
#define bf(v,hi,lo) ((_da(v) >> lo) & ((1 << (hi-lo+1))-1)) : hi-lo+1
#define b(v,p) bf(v,p,p)
#define fill(n,a) (_da(a) ? (1<<n)-1 : 0) : n
#define cat2(a,b) (_da(a) << _sz(b)) + _da(b) : _sz(a) + _sz(b)
#define cat3(a,b,c) cat2(a,cat2(b,c))
#define cat4(a,b,c,d) cat2(a,cat3(b,c,d))
#define cat5(a,b,c,d,e) cat2(a,cat4(b,c,d,e))
#define cat6(a,b,c,d,e,f) cat2(a,cat5(b,c,d,e,f))
#define cat7(a,b,c,d,e,f,g) cat2(a,cat6(b,c,d,e,f,g))
#define cat8(a,b,c,d,e,f,g,h) cat2(a,cat7(b,c,d,e,f,g,h))
#define cat9(a,b,c,d,e,f,g,h,i) cat2(a,cat8(b,c,d,e,f,g,h,i))
#define rs1p cat2(pack(1,2), x(9,7))
#define rs2p cat2(pack(1,2), x(4,2))
#define rs2 x(6,2)
#define rd x(11,7)
#define addi4spnImm cat5(x(10,7), x(12,11), x(5,5), x(6,6), pack(0,2))
#define lwImm cat4(x(5,5), x(12,10), x(6,6), pack(0,2))
#define ldImm cat3(x(6,5), x(12,10), pack(0,3))
#define lwspImm cat4(x(3,2), x(12,12), x(6,4), pack(0,2))
#define ldspImm cat4(x(4,2), x(12,12), x(6,5), pack(0,3))
#define swspImm cat3(x(8,7), x(12,9), pack(0,2))
#define sdspImm cat3(x(9,7), x(12,10), pack(0,3))
#define luiImm cat3(fill(15, x(12,12)), x(6,2), pack(0,12))
#define addi16spImm cat6(fill(3, x(12,12)), x(4,3), x(5,5), x(2,2), x(6,6), pack(0,4))
#define addiImm cat2(fill(7, x(12,12)), x(6,2))
#define jImm cat9(fill(10, x(12,12)), x(8,8), x(10,9), x(6,6), x(7,7), x(2,2), x(11,11), x(5,3), pack(0,1))
#define bImm cat6(fill(5, x(12,12)), x(6,5), x(2,2), x(11,10), x(4,3), pack(0,1))
#define shamt cat2(x(12,12), x(6,2))
#define x0 pack(0,5)
#define ra pack(1,5)
#define sp pack(2,5)
#define ld cat5(ldImm, rs1p, pack(3,3), rs2p, pack(3,7))
#define lw cat5(lwImm, rs1p, pack(2,3), rs2p, pack(3,7))
#define fld cat5(ldImm, rs1p, pack(3,3), rs2p, pack(7,7))
#define flw cat5(lwImm, rs1p, pack(2,3), rs2p, pack(7,7))
#define sd cat6(bf(ldImm,7,5), rs2p, rs1p, pack(3,3), bf(ldImm,4,0), pack(0x23,7))
#define sw cat6(bf(lwImm,6,5), rs2p, rs1p, pack(2,3), bf(lwImm,4,0), pack(0x23,7))
#define fsd cat6(bf(ldImm,7,5), rs2p, rs1p, pack(3,3), bf(ldImm,4,0), pack(0x27,7))
#define fsw cat6(bf(lwImm,6,5), rs2p, rs1p, pack(2,3), bf(lwImm,4,0), pack(0x27,7))
switch (_da(cat2(x(1,0), x(15,13)))) {
// Q0
case 0: {
uint32_t opc = _da(x(12,5)) ? 0x13 : 0x1F;
return _da(cat5(addi4spnImm, sp, pack(0,3), rs2p, pack(opc,7)));
}
case 1: return _da(fld);
case 2: return _da(lw);
case 3: return xlen == 32 ? _da(flw) : _da(ld);
case 4: {
// uimp
return _da(cat6(bf(lwImm,6,5), rs2p, rs1p, pack(2,3), bf(lwImm,4,0), pack(0x3F,7)));
}
case 5: return _da(fsd);
case 6: return _da(sw);
case 7: return xlen == 32 ? _da(fsw) : _da(sd);
// Q1
case 8: return _da(cat5(addiImm, rd, pack(0,3), rd, pack(0x13, 7)));
case 9:
if (xlen == 32)
return _da(cat6(b(jImm,20), bf(jImm,10,1), b(jImm,11), bf(jImm,19,12), ra, pack(0x6F,7)));
return _da(cat5(addiImm, rd, pack(0,3), rd, pack(_da(rd) ? 0x1B : 0x1F,7)));
case 10: return _da(cat5(addiImm, x0, pack(0,3), rd, pack(0x13,7)));
case 11:
if (_da(rd) == 0 || _da(rd) == 2) {
int opc = _da(addiImm) ? 0x13 : 0x1F;
return _da(cat5(addi16spImm, rd, pack(0,3), rd, pack(opc,7)));
} else {
int opc = _da(addiImm) ? 0x37 : 0x3F;
return _da(cat3(bf(luiImm,31,12), rd, pack(opc,7)));
}
case 12:
// arith
switch (_da(x(11,10))) {
case 0: return _da(cat5(shamt, rs1p, pack(5,3), rs1p, pack(0x13,7))); // srli
case 1: return _da(cat5(shamt, rs1p, pack(5,3), rs1p, pack(0x13,7))) | 1<<30; // srai
case 2: return _da(cat5(addiImm, rs1p, pack(7,3), rs1p, pack(0x13,7))); // andi
case 3: { // rtype
// val funct = Seq(0.U, 4.U, 6.U, 7.U, 0.U, 0.U, 2.U, 3.U)(Cat(x(12), x(6,5)))
uint32_t funct = 0x04670023 >> _da(cat3(x(12,12),x(6,5),pack(0,4))) & 0xF;
uint32_t sub = _da(x(6,5)) == 0 ? 1 << 30 : 0;
uint32_t opc = _da(x(12,12)) ? 0x3B : 0x33;
return _da(cat5(rs2p, rs1p, pack(funct, 3), rs1p, pack(opc, 7))) | sub;
}}
case 13: return _da(cat6(b(jImm,20), bf(jImm,10,1), b(jImm,11), bf(jImm,19,12), x0, pack(0x6F,7)));
case 14: return _da(cat8(b(bImm,12), bf(bImm,10,5), x0, rs1p, pack(0,3), bf(bImm,4,1), bf(bImm,11,11), pack(0x63,7)));
case 15: return _da(cat8(b(bImm,12), bf(bImm,10,5), x0, rs1p, pack(1,3), bf(bImm,4,1), bf(bImm,11,11), pack(0x63,7)));
default: return insn;
}
}
@tommythorn
Copy link
Author

tommythorn commented Apr 6, 2020

The solution is to trust the optimizer and use

typedef struct bits_s {
    uint32_t val, size;
} bits_t;

static inline bits_t concatenamebits(bits_t a, bits_t b)
{
    return (bits_t) { a.val << b.size | a.val, a.size + b.size};
}

etc. All the constants gets eliminated and we end up with a good result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment