mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-11 15:21:51 +00:00
- Added some code for generating VM code from FxExpressions. This is completely untested, since
it isn't even used anywhere yet. In retrospect, I probably should have targeted an intermediate representation and done codegen with that instead, since that would be something I can reuse. SVN r1908 (scripting)
This commit is contained in:
parent
e9db2c83a5
commit
3001708d16
8 changed files with 1430 additions and 30 deletions
|
@ -42,12 +42,14 @@
|
|||
|
||||
#include "m_random.h"
|
||||
|
||||
|
||||
#define CHECKRESOLVED() if (isresolved) return this; isresolved=true;
|
||||
#define SAFE_DELETE(p) if (p!=NULL) { delete p; p=NULL; }
|
||||
#define RESOLVE(p,c) if (p!=NULL) p = p->Resolve(c)
|
||||
#define ABORT(p) if (!(p)) { delete this; return NULL; }
|
||||
#define SAFE_RESOLVE(p,c) RESOLVE(p,c); ABORT(p)
|
||||
|
||||
class VMFunctionBuilder;
|
||||
extern PSymbolTable GlobalSymbols;
|
||||
|
||||
//==========================================================================
|
||||
|
@ -142,6 +144,15 @@ struct ExpVal
|
|||
|
||||
};
|
||||
|
||||
struct ExpEmit
|
||||
{
|
||||
ExpEmit() : RegNum(0), RegType(REGT_NIL) {}
|
||||
ExpEmit(int reg, int type) : RegNum(reg), RegType(type) {}
|
||||
ExpEmit(int reg, int type, bool konst) : RegNum(reg), RegType(type), Konst(konst) {}
|
||||
ExpEmit(VMFunctionBuilder *build, int type);
|
||||
|
||||
BYTE RegNum, RegType, Konst;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -166,12 +177,25 @@ public:
|
|||
virtual bool isConstant() const;
|
||||
virtual void RequestAddress();
|
||||
|
||||
virtual ExpEmit Emit(VMFunctionBuilder *build);
|
||||
|
||||
FScriptPosition ScriptPosition;
|
||||
FExpressionType ValueType;
|
||||
|
||||
bool isresolved;
|
||||
};
|
||||
|
||||
class FxParameter : public FxExpression
|
||||
{
|
||||
FxExpression *Operand;
|
||||
|
||||
public:
|
||||
FxParameter(FxExpression*);
|
||||
~FxParameter();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxIdentifier
|
||||
|
@ -310,6 +334,21 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
class FxFloatCast : public FxExpression
|
||||
{
|
||||
FxExpression *basex;
|
||||
|
||||
public:
|
||||
|
||||
FxFloatCast(FxExpression *x);
|
||||
~FxFloatCast();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
|
||||
|
@ -327,6 +366,7 @@ public:
|
|||
FxPlusSign(FxExpression*);
|
||||
~FxPlusSign();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -344,6 +384,7 @@ public:
|
|||
~FxMinusSign();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -361,6 +402,7 @@ public:
|
|||
~FxUnaryNotBitwise();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -378,6 +420,7 @@ public:
|
|||
~FxUnaryNotBoolean();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -396,6 +439,7 @@ public:
|
|||
FxBinary(int, FxExpression*, FxExpression*);
|
||||
~FxBinary();
|
||||
bool ResolveLR(FCompileContext& ctx, bool castnumeric);
|
||||
void Promote();
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -411,6 +455,7 @@ public:
|
|||
FxAddSub(int, FxExpression*, FxExpression*);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -426,6 +471,7 @@ public:
|
|||
FxMulDiv(int, FxExpression*, FxExpression*);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -441,6 +487,7 @@ public:
|
|||
FxCompareRel(int, FxExpression*, FxExpression*);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -456,6 +503,7 @@ public:
|
|||
FxCompareEq(int, FxExpression*, FxExpression*);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -471,6 +519,7 @@ public:
|
|||
FxBinaryInt(int, FxExpression*, FxExpression*);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -491,6 +540,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -511,6 +561,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -530,6 +581,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -551,6 +603,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -564,6 +617,7 @@ class FxFRandom : public FxRandom
|
|||
public:
|
||||
FxFRandom(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -584,6 +638,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
|
||||
|
@ -623,6 +678,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
void RequestAddress();
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -637,6 +693,7 @@ public:
|
|||
FxSelf(const FScriptPosition&);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -657,6 +714,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
//void RequestAddress();
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
|
||||
|
@ -700,6 +758,7 @@ public:
|
|||
~FxActionSpecialCall();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -719,6 +778,7 @@ public:
|
|||
~FxGlobalFunctionCall();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
|
||||
|
@ -739,6 +799,7 @@ public:
|
|||
~FxClassTypeCast();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -775,6 +836,7 @@ public:
|
|||
FxMultiNameState(const char *statestring, const FScriptPosition &pos);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpVal EvalExpression (AActor *self);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -107,6 +107,7 @@ enum
|
|||
ATAG_AREGISTER, // pointer to an address register
|
||||
|
||||
ATAG_STATE, // pointer to FState
|
||||
ATAG_RNG, // pointer to FRandom
|
||||
};
|
||||
|
||||
class VMFunction : public DObject
|
||||
|
|
|
@ -61,10 +61,10 @@ VMScriptFunction *VMFunctionBuilder::MakeFunction()
|
|||
}
|
||||
|
||||
// Assign required register space.
|
||||
func->NumRegD = IntRegisters.MostUsed;
|
||||
func->NumRegF = FloatRegisters.MostUsed;
|
||||
func->NumRegA = AddressRegisters.MostUsed;
|
||||
func->NumRegS = StringRegisters.MostUsed;
|
||||
func->NumRegD = Registers[REGT_INT].MostUsed;
|
||||
func->NumRegF = Registers[REGT_FLOAT].MostUsed;
|
||||
func->NumRegA = Registers[REGT_POINTER].MostUsed;
|
||||
func->NumRegS = Registers[REGT_STRING].MostUsed;
|
||||
func->MaxParam = MaxParam;
|
||||
|
||||
// Technically, there's no reason why we can't end the function with
|
||||
|
@ -274,7 +274,7 @@ VMFunctionBuilder::RegAvailability::RegAvailability()
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailibity :: GetReg
|
||||
// VMFunctionBuilder :: RegAvailibity :: Get
|
||||
//
|
||||
// Gets one or more unused registers. If getting multiple registers, they
|
||||
// will all be consecutive. Returns -1 if there were not enough consecutive
|
||||
|
@ -286,7 +286,7 @@ VMFunctionBuilder::RegAvailability::RegAvailability()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int VMFunctionBuilder::RegAvailability::GetReg(int count)
|
||||
int VMFunctionBuilder::RegAvailability::Get(int count)
|
||||
{
|
||||
VM_UWORD mask;
|
||||
int i, firstbit;
|
||||
|
@ -368,13 +368,13 @@ int VMFunctionBuilder::RegAvailability::GetReg(int count)
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailibity :: ReturnReg
|
||||
// VMFunctionBuilder :: RegAvailibity :: Return
|
||||
//
|
||||
// Marks a range of registers as free again.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::RegAvailability::ReturnReg(int reg, int count)
|
||||
void VMFunctionBuilder::RegAvailability::Return(int reg, int count)
|
||||
{
|
||||
assert(count >= 1 && count <= 32);
|
||||
assert(reg >= 0 && reg + count <= 256);
|
||||
|
@ -406,8 +406,108 @@ void VMFunctionBuilder::RegAvailability::ReturnReg(int reg, int count)
|
|||
}
|
||||
}
|
||||
|
||||
void VMFunctionBuilder::RegAvailability::Dump()
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: Emit
|
||||
//
|
||||
// Just dumbly output an instruction. Returns instruction position, not
|
||||
// byte position. (Because all instructions are exactly four bytes long.)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc)
|
||||
{
|
||||
Printf("%032B %032B %032B %032B\n%032B %032B %032B %032B\n",
|
||||
Used[0], Used[1], Used[2], Used[3], Used[4], Used[5], Used[6], Used[7]);
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opa >= 0 && opa <= 255);
|
||||
assert(opb >= 0 && opb <= 255);
|
||||
assert(opc >= 0 && opc <= 255);
|
||||
size_t loc = Code.Reserve(4);
|
||||
VM_UBYTE *code = &Code[loc];
|
||||
code[0] = opcode;
|
||||
code[1] = opa;
|
||||
code[2] = opb;
|
||||
code[3] = opc;
|
||||
return loc / 4;
|
||||
}
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opa, VM_SHALF opbc)
|
||||
{
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opa >= 0 && opa <= 255);
|
||||
assert(opbc >= -32768 && opbc <= 32767);
|
||||
size_t loc = Code.Reserve(4);
|
||||
VM_UBYTE *code = &Code[loc];
|
||||
code[0] = opcode;
|
||||
code[1] = opa;
|
||||
*(VM_SHALF *)&code[2] = opbc;
|
||||
return loc / 4;
|
||||
}
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opabc)
|
||||
{
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opabc >= -(1 << 23) && opabc <= (1 << 24) - 1);
|
||||
size_t loc = Code.Reserve(4);
|
||||
#ifdef __BIG_ENDIAN__
|
||||
*(VM_UWORD *)&Code[loc] = (opabc & 0xFFFFFF) | (opcode << 24);
|
||||
#else
|
||||
*(VM_UWORD *)&Code[loc] = opcode | (opabc << 8);
|
||||
#endif
|
||||
return loc / 4;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: EmitLoadInt
|
||||
//
|
||||
// Loads an integer constant into a register, using either an immediate
|
||||
// value or a constant register, as appropriate.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::EmitLoadInt(int regnum, int value)
|
||||
{
|
||||
assert(regnum >= 0 && regnum < Registers[REGT_INT].MostUsed);
|
||||
if (value >= -32768 && value <= 32767)
|
||||
{
|
||||
return Emit(OP_LI, regnum, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Emit(OP_LK, regnum, GetConstantInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: Backpatch
|
||||
//
|
||||
// Store a JMP instruction at <loc> that points at <target>.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::Backpatch(size_t loc, size_t target)
|
||||
{
|
||||
assert(loc < Code.Size() / 4);
|
||||
int offset = int(target - loc - 1);
|
||||
assert(offset >= -(1 << 24) && offset <= (1 << 24) - 1);
|
||||
#ifdef __BIG_ENDIAN__
|
||||
*(VM_UWORD *)&Code[loc * 4] = (offset & 0xFFFFFF) | (OP_JMP << 24);
|
||||
#else
|
||||
*(VM_UWORD *)&Code[loc * 4] = OP_JMP | (offset << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: BackpatchToHere
|
||||
//
|
||||
// Store a JMP instruction at <loc> that points to the current code gen
|
||||
// location.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::BackpatchToHere(size_t loc)
|
||||
{
|
||||
Backpatch(loc, Code.Size() / 4);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,8 @@ public:
|
|||
{
|
||||
public:
|
||||
RegAvailability();
|
||||
int GetReg(int count); // Returns the first register in the range
|
||||
void ReturnReg(int reg, int count);
|
||||
void Dump();
|
||||
int Get(int count); // Returns the first register in the range
|
||||
void Return(int reg, int count);
|
||||
|
||||
private:
|
||||
VM_UWORD Used[256/32]; // Bitmap of used registers (bit set means reg is used)
|
||||
|
@ -35,6 +34,12 @@ public:
|
|||
|
||||
// Returns the address of the newly-emitted instruction.
|
||||
size_t Emit(int opcode, int opa, int opb, int opc);
|
||||
size_t Emit(int opcode, int opa, VM_SHALF opbc);
|
||||
size_t Emit(int opcode, int opabc);
|
||||
size_t EmitLoadInt(int regnum, int value);
|
||||
|
||||
void Backpatch(size_t addr, size_t target);
|
||||
void BackpatchToHere(size_t addr);
|
||||
|
||||
// Write out complete constant tables.
|
||||
void FillIntConstants(int *konst);
|
||||
|
@ -46,10 +51,10 @@ public:
|
|||
void ParamChange(int delta);
|
||||
|
||||
// Track available registers.
|
||||
RegAvailability IntRegisters;
|
||||
RegAvailability FloatRegisters;
|
||||
RegAvailability AddressRegisters;
|
||||
RegAvailability StringRegisters;
|
||||
RegAvailability Registers[4];
|
||||
|
||||
// Free a register.
|
||||
void FreeReg(int regtype, int regnum);
|
||||
|
||||
private:
|
||||
struct AddrKonst
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
#define JMPOFS(x) ((*(VM_SWORD *)(x) >> 6) & ~3)
|
||||
#endif
|
||||
|
||||
#define KC (konst[C])
|
||||
#define RC (reg.i[C])
|
||||
#define KC (konstd[C])
|
||||
#define RC (reg.d[C])
|
||||
|
||||
#define PA (reg.a[A])
|
||||
#define PB (reg.a[B])
|
||||
|
@ -61,11 +61,12 @@ enum
|
|||
X_READ_NIL,
|
||||
X_WRITE_NIL,
|
||||
X_TOO_MANY_TRIES,
|
||||
X_ARRAY_OUT_OF_BOUNDS
|
||||
};
|
||||
|
||||
#define GETADDR(a,o,x) \
|
||||
if (a == 0) { THROW(x); } \
|
||||
ptr = (VM_SBYTE *)a + x \
|
||||
if (a == NULL) { THROW(x); } \
|
||||
ptr = (VM_SBYTE *)a + o
|
||||
|
||||
static const VM_UWORD ZapTable[16] =
|
||||
{
|
||||
|
|
|
@ -586,6 +586,13 @@ begin:
|
|||
assert(0);
|
||||
NEXTOP;
|
||||
|
||||
OP(BOUND):
|
||||
if (reg.d[a] >= BC)
|
||||
{
|
||||
THROW(X_ARRAY_OUT_OF_BOUNDS);
|
||||
}
|
||||
NEXTOP;
|
||||
|
||||
OP(CONCAT):
|
||||
ASSERTS(a); ASSERTS(B); ASSERTS(C);
|
||||
{
|
||||
|
|
|
@ -73,7 +73,7 @@ xx(DYNCAST_K, dyncast,RPRPKP),
|
|||
xx(TEST, test, RII16), // if (dA != BC) then pc++
|
||||
xx(JMP, jmp, I24), // pc += ABC -- The ABC fields contain a signed 24-bit offset.
|
||||
xx(IJMP, ijmp, RII16), // pc += dA + BC -- BC is a signed offset. The target instruction must be a JMP.
|
||||
xx(PARAM, param, __BCP), // push parameter encoded in BC for function call or result for return
|
||||
xx(PARAM, param, __BCP), // push parameter encoded in BC for function call (B=regtype, C=regnum)
|
||||
xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C
|
||||
xx(CALL_K, call, KPI8I8),
|
||||
xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL)
|
||||
|
@ -87,6 +87,7 @@ xx(CATCH, catch, CATCH), // A == 0: continue search on next try
|
|||
// A == 2: (pB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
|
||||
// A == 3: (pkB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
|
||||
// for A > 0, exception is stored in pC
|
||||
xx(BOUND, bound, RII16), // if rA >= BC, throw exception
|
||||
|
||||
// String instructions.
|
||||
xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC
|
||||
|
@ -129,7 +130,7 @@ xx(MAX_RR, max, RIRIRI), // dA = max(dB,dkC)
|
|||
xx(MAX_RK, max, RIRIKI),
|
||||
xx(ABS, abs, RIRI), // dA = abs(dB)
|
||||
xx(NEG, neg, RIRI), // dA = -dB
|
||||
xx(NOT, not, RIRI), // dA = !dB
|
||||
xx(NOT, not, RIRI), // dA = ~dB
|
||||
xx(SEXT, sext, RIRII8), // dA = dB, sign extended by shifting left then right by C
|
||||
xx(ZAP_R, zap, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are one
|
||||
xx(ZAP_I, zap, RIRII8),
|
||||
|
|
Loading…
Reference in a new issue