URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
Compare Revisions
- This comparison shows the changes necessary to convert path
/forwardcom
- from Rev 43 to Rev 44
- ↔ Reverse comparison
Rev 43 → Rev 44
/bintools/assem4.cpp
0,0 → 1,2134
/**************************** assem4.cpp ******************************** |
* Author: Agner Fog |
* Date created: 2017-04-17 |
* Last modified: 2021-07-14 |
* Version: 1.11 |
* Project: Binary tools for ForwardCom instruction set |
* Module: assem.cpp |
* Description: |
* Module for assembling ForwardCom .as files. |
* This module contains: |
* pass3(): Interpretation of code lines. |
* Copyright 2017-2021 GNU General Public License http://www.gnu.org/licenses |
******************************************************************************/ |
#include "stdafx.h" |
|
|
// Interpret lines. Generate code and data |
void CAssembler::pass3() { |
uint16_t last_line_type = 0; // type of preceding line |
makeFormatLists(); // make formatList3 and formatList4 |
code_size = cmd.codeSizeOption; // initialize options |
data_size = cmd.dataSizeOption; |
section = 0; |
iLoop = iIf = iSwitch = 0; // index of current high level statements |
|
// lines loop |
for (linei = 1; linei < lines.numEntries()-1; linei++) { |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
if (tokenN == 0 || lines[linei].type == LINE_ERROR || lines[linei].type == LINE_METADEF) continue; |
lineError = false; |
|
switch (lines[linei].type) { |
case LINE_DATADEF: |
if (last_line_type == LINE_CODEDEF && (lines[linei].sectionType & SHF_EXEC)) { |
/* currently, the assembler cannot mix code and data because they are put in different buffers. |
The only way to hard-code instructions is to put them into a separate section. */ |
errors.reportLine(ERR_MIX_DATA_AND_CODE); // data definition in code section |
} |
break; |
case LINE_CODEDEF: |
interpretCodeLine(); |
if (last_line_type == LINE_DATADEF && !(lines[linei].sectionType & SHF_EXEC)) { |
errors.reportLine(ERR_MIX_DATA_AND_CODE); // code definition in data section |
} |
break; |
case LINE_METADEF: case LINE_ERROR: |
continue; |
case LINE_FUNCTION: |
interpretFunctionDirective(); |
break; |
case LINE_SECTION: |
interpretSectionDirective(); |
break; |
case LINE_ENDDIR: |
interpretEndDirective(); |
break; |
case LINE_OPTIONS: |
interpretOptionsLine(); |
break; |
} |
|
last_line_type = lines[linei].type; |
} |
while (hllBlocks.numEntries()) { |
// unfinished block |
SBlock block = hllBlocks.pop(); |
errors.report(tokens[block.startBracket].pos, tokens[block.startBracket].stringLength, ERR_BRACKET_BEGIN); |
} |
} |
|
// extract subsets of formatList (in disasm1.cpp) for multiformat instructions and jump instructions |
void CAssembler::makeFormatLists() { |
uint32_t i; |
for (i = 0; i < formatListSize; i++) { |
if (formatList[i].category == 3) formatList3.push(formatList[i]); |
if (formatList[i].category == 4) formatList4.push(formatList[i]); |
} |
} |
|
// Interpret a line defining code. This covers both assembly style and high level style code |
void CAssembler::interpretCodeLine() { |
uint32_t tok; // token index |
dataType = 0; // data type for current instruction |
uint32_t nReg = 0; // number of register source operands |
uint32_t state = 0; /* state during interpretation of line. example: |
L1: int32 r1 = compare(r2, 5), option = 2 // assembly style |
L1: int32 r1 = r2 < 5 // same in high level style |
0: begin |
1: after label |
2: after label: |
3: after type |
4: after destination |
5: after destination = (expecting expression or instruction) |
6: after expression or instruction() |
7: after instruction |
8: after instruction( |
9: after operand |
10: after instruction(), |
11: after jump instruction |
*/ |
SExpression expr; // evaluated expression |
SCode code; // current instruction code |
zeroAllMembers(code); // reset code structure |
|
if (section == 0) { |
errors.reportLine(ERR_CODE_WO_SECTION); |
} |
|
// high level instructions with nothing before can be caught already here |
if (tokens[tokenB].type == TOK_HLL) { |
interpretHighLevelStatement(); // if, else, switch, for, do, while (){} statements |
return; |
} |
if (tokens[tokenB].type == TOK_OPR && tokens[tokenB].id == '}') { |
interpretEndBracket(); // end of {} block |
return; |
} |
|
// interpret line by state machine looping through tokens |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
SToken token = tokens[tok]; |
if (token.type == TOK_XPR && expressions[token.value.w].etype & XPR_REG) { |
// this is an alias for a register. Translate to register |
token.type = TOK_REG; |
token.id = expressions[token.value.w].reg1; |
} |
|
if (lineError) break; |
code.section = section; |
|
if (state == 5) { // after '=' |
if (token.type == TOK_INS) { // instruction |
if (code.instruction) errors.report(token); // instruction after += etc. |
code.instruction = token.id; |
state = 7; |
} |
else { // expression after equal sign |
// interpret expression representing operands and operator |
expr = expression(tok, tokenB + tokenN - tok, 0); |
if (lineError) return; |
if (code.instruction) { |
// += operator etc. already encountered. combine the operands |
uint32_t op = code.instruction; code.instruction = 0; |
code.reg1 = code.dest; // first source operand is same as destination |
code.etype |= XPR_REG1; code.tokens = 0; |
expr = op2(op, code, expr); // operation '+' for '+=', etc. |
code.instruction = 0; code.reg1 = 0; |
} |
if (code.etype & XPR_ERROR) { |
errors.reportLine(code.value.w); // report error |
} |
// ordinary '=' goes here |
if (lineError) return; |
insertAll(code, expr); |
tok += expr.tokens - 1; |
state = 6; |
} |
} |
else if (state == 11) { |
// interpret jump target |
expr = expression(tok, tokenB + tokenN - tok, 0); |
state = 6; |
if (expr.etype & XPR_REG) { |
code = code | expr; |
tok += expr.tokens - 1; |
} |
else if (expr.etype & (XPR_INT | XPR_SYM1)) { |
code.sym5 = expr.sym3 ? expr.sym3 : expr.sym1; |
code.offset_jump = expr.value.w; |
if (expr.value.w & 3) errors.report(token.pos, token.stringLength, ERR_JUMP_TARGET_MISALIGN); |
tok += expr.tokens - 1; |
code.etype |= XPR_JUMPOS | (expr.etype & ~XPR_IMMEDIATE); |
} |
else { |
errors.report(token.pos, token.stringLength, ERR_EXPECT_JUMP_TARGET); |
break; |
} |
} |
else if (state == 8 && token.type != TOK_OPT && token.type != TOK_REG) { |
// expression in parameter list |
if (token.type == TOK_OPR && token.id == ')') { |
state = 6; break; // end of parameter list |
} |
// interpret any expression, except register or option |
expr = expression(tok, tokenB + tokenN - tok, 0); |
tok += expr.tokens - 1; |
if (code.etype & expr.etype & XPR_INT) { |
// multiple immediate integer constants |
if (code.etype & XPR_INT2) { |
// three integer operands |
if (code.etype & XPR_OPTIONS) errors.report(token.pos, token.stringLength, ERR_TOO_MANY_OPERANDS); |
code.optionbits = uint8_t(expr.value.w); |
code.etype |= XPR_OPTIONS; |
expr.value.u = 0; |
} |
else { |
// two integer operands |
if (code.value.u >> 32 != 0) errors.report(token.pos, token.stringLength, ERR_TOO_MANY_OPERANDS); |
code.value.u = code.value.w | expr.value.u << 32; |
code.etype |= XPR_INT2; |
expr.value.u = 0; |
} |
} |
else if (expr.etype & XPR_MEM) { |
if (expr.etype & XPR_OFFSET) code.offset_mem += expr.offset_mem; |
//else code.offset += expr.value.i; |
if (expr.etype & XPR_IMMEDIATE) { // both memory and immediate operands |
code.value.i = expr.value.i; |
} |
} |
else if (expr.etype & XPR_IMMEDIATE) { |
code.value.i = expr.value.i; |
} |
expr.value.i = 0; |
code = code | expr; |
state = 9; |
} |
else { |
switch (token.type) { |
case TOK_LAB: case TOK_SYM: |
if (state == 0) { |
//code.label = token.value.w; |
code.label = token.id; |
if (code.label) { |
int32_t symi = findSymbol(code.label); |
if (symi > 0) symbols[symi].st_section = section; |
} |
state = 1; |
} |
else goto ST_ERROR; |
break; |
case TOK_OPR: |
if (token.id == ':' && state == 1) { |
state = 2; |
} |
else if (token.id == '+' && state == 3) { |
code.dtype |= TYP_PLUS; |
} |
else if (token.priority == 15 && state == 4) { |
// assignment operator |
state = 5; |
if (token.id & EQ) { // combined operator and assignment: += -= *= etc. |
code.reg1 = code.dest; |
code.etype |= XPR_REG | XPR_REG1; |
code.instruction = token.id & ~EQ; // temporarily store operator in .instruction |
} |
else if (token.id != '=') errors.report(token); |
} |
else if (token.id == '=' && state == 11) { |
state = 12; |
} |
else if (token.id == ',' && state == 6) { |
state = 10; |
} |
else if (token.id == ',' && state == 9) { |
state = 8; |
} |
else if (token.id == '(' && state == 7) { |
state = 8; |
} |
else if (token.id == ')' && (state == 8 || state == 9)) { |
state = 6; |
} |
else if (token.id == '[' && (state == 0 || state == 2 || state == 3)) { |
// interpret memory destination |
expr = expression(tok, tokenB + tokenN - tok, 0); |
tok += expr.tokens - 1; |
insertMem(code, expr); |
code.dest = 2; |
state = 4; |
} |
else if (token.id == '[' && state == 7 && code.instruction == II_ADDRESS) { |
// address []. expect memory operand |
expr = expression(tok, tokenB + tokenN - tok, 0); |
tok += expr.tokens - 1; |
insertMem(code, expr); |
state = 6; |
} |
else if ((token.id == '+' + D2 || token.id == '-' + D2) && (state == 3 || state == 4)) { |
// ++ and -- operators |
code.instruction = (token.id == '+' + D2) ? II_ADD : II_SUB; |
// operand is 1, integer or float |
if (dataType & TYP_FLOAT) { |
code.value.d = 1.0; |
code.etype |= XPR_FLT; |
} |
else { |
code.value.i = 1; |
code.etype |= XPR_INT; |
} |
if (state == 3) { // prefix operator. expect register |
tok++; |
if (token.type != TOK_REG) errors.report(token); |
code.dest = token.id; |
} |
code.reg1 = code.dest; |
code.etype |= XPR_REG1; |
state = 6; |
} |
else if (token.id == ';') {} // ignore terminating ';' |
else goto ST_ERROR; |
break; |
case TOK_TYP: |
if (state == 0 || state == 2) { |
dataType = code.dtype = token.id; |
state = 3; |
} |
else goto ST_ERROR; |
break; |
case TOK_REG: |
if (state == 0 || state == 2 || state == 3) { |
code.dest = uint8_t(token.id); |
state = 4; |
} |
else if (state == 8) { |
if (nReg < 3) { |
(&code.reg1)[nReg] = (uint8_t)token.id; // insert register in expression |
code.etype |= XPR_REG1 << nReg++; |
if ((code.etype & (XPR_INT | XPR_FLT | XPR_MEM)) && code.dest != 2) errors.report(token.pos, token.stringLength, ERR_OPERANDS_WRONG_ORDER); |
} |
else errors.report(token.pos, token.stringLength, ERR_TOO_MANY_OPERANDS); |
state = 9; |
} |
else goto ST_ERROR; |
break; |
case TOK_XPR: |
if (token.value.u >= expressions.numEntries()) goto ST_ERROR; // expression not found |
if (expressions[token.value.w].etype & XPR_MEM) { // this is an alias for a memory operand |
insertMem(code, expressions[token.value.w]); |
code.dest = 2; |
state = 4; |
} |
else goto ST_ERROR; |
break; |
case TOK_INS: |
if (state == 0 || state == 2 || state == 3) { |
// interpret instruction name |
code.instruction = token.id; |
state = 7; // expect parenthesis and parameters |
if (code.instruction & II_JUMP_INSTR) { |
// Jump or call instruction. The next may be a jump target, a register or a memory operand |
state = 11; // expect jump target |
// Check if there is a memory operand |
for (uint32_t tok2 = tok+1; tok2 < tokenB + tokenN; tok2++) { |
if (tokens[tok2].type == TOK_OPR && tokens[tok2].id == '[') { |
// a jump instruction with memory operand is treated as a normal instruction |
state = 7; break; |
} |
} |
} |
} |
else if ((state == 6 || state == 10) && (token.id & II_JUMP_INSTR)) { |
// second half of jump instruction |
code.instruction |= token.id; // combine two partial instruction names |
state = 11; // expect jump target |
} |
else goto ST_ERROR; |
break; |
case TOK_OPT: // option keyword |
expr = expression(tok, tokenB + tokenN - tok, 4); // this will read option = value |
tok += expr.tokens - 1; |
code.etype |= expr.etype; |
if (expr.etype & XPR_LIMIT) { |
code.value.i = expr.value.i; |
if (expr.value.u >= 0x100000000U) { // limit too high |
errors.report(tokens[tok - 1].pos, tokens[tok - 1].stringLength, ERR_LIMIT_TOO_HIGH); |
} |
} |
if (expr.etype & (XPR_LENGTH | XPR_BROADC)) code.length = expr.length; |
if (expr.etype & XPR_MASK) code.mask = expr.mask; |
if (expr.etype & XPR_FALLBACK) code.fallback = expr.fallback; |
if (expr.etype & XPR_OPTIONS) code.optionbits = expr.optionbits; |
if (state == 8) state = 9; |
else if (state == 6 || state == 10) state = 6; |
else goto ST_ERROR; |
break; |
case TOK_ATT: |
if (token.id == ATT_ALIGN && state == 0 && tokenN >= 2) { |
// align n directive |
code.instruction = II_ALIGN; |
expr = expression(tok + 1, tokenB + tokenN - tok - 1, 0); |
tok = tokenB + tokenN; |
code.value.u = expr.value.u; |
code.sizeUnknown = 0x80; |
if ((code.value.u & (code.value.u - 1)) || code.value.u > MAX_ALIGN |
|| (expr.etype & XPR_IMMEDIATE) != XPR_INT || (expr.etype & (XPR_REG|XPR_OPTION|XPR_MEM))) { |
errors.reportLine(ERR_ALIGNMENT); |
} |
} |
else goto ST_ERROR; |
break; |
case TOK_HLL: // high level directive: if, else, while, for, etc. |
interpretHighLevelStatement(); |
return; |
default:; |
ST_ERROR: |
errors.report(token); |
break; |
} |
} |
} |
if (lineError) return; |
// check if state machine ends with a finished instruction |
if (state != 0 && state != 2 && state != 6 && state != 7) { |
errors.report(tokens[tok-1].pos, tokens[tok-1].stringLength, ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
|
// move and store instruction has no operator yet |
if (code.instruction == 0 && code.etype) { |
if (code.dest == 2) code.instruction = II_STORE; // store to memory |
else { |
code.instruction = II_MOVE; // move constant to register |
if (cmd.optiLevel && (code.etype & XPR_INT) && code.value.i >= 0 && !code.sym3 && (code.dtype & TYP_INT) && (code.dest & REG_R)) { |
code.dtype |= TYP_PLUS; // optimize to larger type for positive constant because it is zero-extended anyway |
} |
} |
} |
|
if (code.instruction) { // a code record with no instruction represents a label only |
// code record contains instruction |
if (code.etype & XPR_JUMPOS) mergeJump(code); |
|
checkCode1(code); |
if (lineError) return; |
|
// find an instruction variant that fits |
fitCode(code); |
if (lineError) return; |
} |
|
// save code structure |
codeBuffer.push(code); |
} |
|
|
// Check how many bits are needed to contain immediate constant of an instruction. |
// The result is returned as bit-flags in code.fitNumX. |
// The return value is nonzero if the size cannot be resolved yet. |
int CAssembler::fitConstant(SCode & code) { |
int64_t value = 0; // the constant or address to fit |
int64_t valueScaled; // value divided by scale factor |
double dvalue = 0; // floating point value if needed |
bool floatType = false; // a floating point type is needed |
bool floatConst = false; // a floating point constant is provided |
uint32_t fitNum = 0; // return value |
uint32_t sym3 = 0, sym4 = 0; // symbols |
int32_t isym3 = 0, isym4 = 0; // symbol index |
int32_t uncertainty; // maximum deviance if the value is uncertain |
int uncertain = 0; // return value |
int symscale; // scaling of difference between symbols |
|
if (code.instruction == II_ALIGN) return 0; // not an instruction |
if (!(code.etype & (XPR_IMMEDIATE | XPR_SYM1))) return 0; // has no immediate |
|
value = value0 = code.value.i; // immediate constant |
floatType = uint8_t(code.dtype) >= uint8_t(TYP_FLOAT16); // floating point needed |
floatConst = (code.etype & XPR_FLT) != 0; // floating point provided |
if (floatType) { |
if (floatConst) dvalue = code.value.d; |
else { |
// Note: We are converting the immediate constant to floating point here in order to find |
// the optimal representation. We have not identified the instruction yet so we don't know |
// if it actually needs a floating point constant or an integer. We have saved the original |
// integer value in value0 so that we can undo the conversion in case an instruction with |
// floating point type needs an integer operand. |
dvalue = (double)value; // value as float |
if (code.etype & XPR_INT) { |
// convert integer constant to float |
code.value.d = dvalue; |
code.etype = (code.etype & ~XPR_IMMEDIATE) | XPR_FLT; |
floatConst = true; |
} |
} |
if ((code.etype & XPR_FLT) && uint8_t(code.dtype) == uint8_t(TYP_FLOAT32)) { |
union { // check for overflow in single precision float |
float f; |
uint32_t i; |
} u; |
u.f = float(code.value.d); |
if (isinf_f(u.i) && u.f > code.value.d) errors.reportLine(ERR_CONSTANT_TOO_LARGE); |
} |
if ((code.etype & XPR_FLT) && uint8_t(code.dtype) == uint8_t(TYP_FLOAT16)) { |
// check for overflow in half precision float |
if (isinf_h(double2half(code.value.d) && !isinf_d(code.value.i))) errors.reportLine(ERR_CONSTANT_TOO_LARGE); |
} |
} |
|
// check for symbols |
if (code.sym3) { |
sym3 = code.sym3; sym4 = code.sym4; |
symscale = code.symscale3; |
isym3 = findSymbol(sym3); |
if (isym3 < 1) { |
code.sizeUnknown = 2; return 2; // should not occur |
} |
} |
|
if (code.sym3 && !code.sym4 && int32_t(symbols[isym3].st_section) == SECTION_LOCAL_VAR && symbols[isym3].st_type == STT_CONSTANT) { |
// convert local symbol to constant |
value = symbols[isym3].st_value; |
code.value.i = value; |
code.sym3 = 0; |
if (cmd.optiLevel && value >= 0 && (code.dtype & TYP_INT) && (code.dest & REG_R)) { |
code.dtype |= TYP_PLUS; // optimize to larger type for positive constant because it is zero-extended anyway |
} |
} |
else if (sym3) { |
// there is a symbol |
if (symbols[isym3].st_unitsize == 0) uncertain = 2; // symbol value is not known yet |
uint32_t sym3section = symbols[isym3].st_section; // symbol section |
// determine necessary relocation size if relocation needed |
uint64_t relSize; // maximum size of relocated address |
if (symbols[isym3].st_type == STT_CONSTANT) { |
relSize = 0x10000000; // there is no command line option for the size of absolute symbols. assume 32 bit |
code.etype |= XPR_INT; |
} |
else if (sym3section && symbols[isym3].st_type != STT_CONSTANT) { // local symbol with known section |
relSize = (sectionHeaders[sym3section].sh_flags & (SHF_EXEC | SHF_IP)) ? code_size : data_size; |
} |
else { // external symbol with unknown section. look at symbol attributes |
relSize = (symbols[isym3].st_other & (STV_EXEC | STV_IP)) ? code_size : data_size; |
if (!(code.etype & (XPR_MEM | XPR_SYM2))) { |
errors.reportLine(ERR_CONFLICT_TYPE); // must be memory operand |
} |
} |
if (sym4) { |
// value is (sym3 - sym4) / scale factor |
isym4 = findSymbol(sym4); |
if (isym4 <= 0) { |
code.sizeUnknown = 2; return 2; // should not occur |
} |
code.etype |= XPR_INT; // symbol difference gives an integer |
if (symbols[isym3].st_unitsize == 0) uncertain = 2; // symbol value is not known yet |
if (symbols[isym3].st_section != symbols[isym4].st_section || symbols[isym3].st_bind != STB_LOCAL || symbols[isym4].st_bind != STB_LOCAL) { |
// different sections or not local. relocation needed |
fitNum = IFIT_RELOC; |
if (code.symscale1 > 1) relSize /= code.symscale1; // value is scaled |
if (relSize <= 1 << 7) fitNum |= IFIT_I8; |
if (relSize <= 1 << 15) fitNum |= IFIT_I16; |
if (relSize <= (uint64_t)1 << 31) fitNum |= IFIT_I32; |
code.fitNum = fitNum; |
code.sizeUnknown = uncertain; |
return uncertain; |
} |
// difference between two local symbols |
if (pass < 4) { |
code.fitNum = IFIT_I8 | IFIT_I16 | IFIT_I32; // symbol values are not available yet |
code.sizeUnknown = 1; |
return 1; |
} |
value += int32_t(uint32_t(symbols[isym3].st_value) - uint32_t(symbols[isym4].st_value)); |
if (symscale < 1) symscale = 1; |
valueScaled = value / symscale + code.offset_mem; |
if (valueScaled >= -(1 << 7) && valueScaled < (1 << 7)) fitNum |= IFIT_I8; |
if (valueScaled >= -(1 << 15) && valueScaled < (1 << 15)) fitNum |= IFIT_I16; |
if (valueScaled >= -((int64_t)1 << 31) && valueScaled < ((int64_t)1 << 31)) fitNum |= IFIT_I32; |
// check if value is certain. uncertainty is stored in high part of st_value |
uncertainty = (symbols[isym3].st_value >> 32) - (symbols[isym4].st_value >> 32); |
valueScaled = value / symscale + code.offset_mem + uncertainty; |
if (symscale > 1) valueScaled /= symscale; // value is scaled |
if ((valueScaled < -(1 << 7) || valueScaled >= (1 << 7)) && (fitNum & IFIT_I8)) uncertain |= 1; |
if ((valueScaled < -(1 << 15) || valueScaled >= (1 << 15)) && (fitNum & IFIT_I16)) uncertain |= 1; |
if ((valueScaled < -((int64_t)1 << 31) || valueScaled >= ((int64_t)1 << 31)) && (fitNum & IFIT_I32)) uncertain |= 1; |
|
if (uncertain && (code.fitNum & IFIT_LARGE)) { |
// choose the larger version if optimization process has convergence problems |
fitNum = (fitNum & (fitNum - 1)) | IFIT_I32; // remove the lowest set bit |
uncertain &= ~1; |
} |
code.fitNum = fitNum; |
code.sizeUnknown = uncertain; |
return uncertain; |
} |
// one symbol. must be constant |
if (sym3section != 0 && symbols[isym3].st_type != STT_CONSTANT && !(code.etype & XPR_MEM)) { |
errors.reportLine(ERR_MEM_WO_BRACKET); |
return 1; |
} |
|
if (sym3section && symbols[isym3].st_type != STT_CONSTANT && (sectionHeaders[sym3section].sh_flags & SHF_IP)) { |
// relative to instruction pointer |
if (sym3section != code.section || symbols[isym3].st_bind != STB_LOCAL) { |
// symbol is in different section or not local. relocation needed |
fitNum = IFIT_RELOC; |
if (relSize <= 1 << 7) fitNum |= IFIT_I8; // necessary relocation size |
if (relSize <= 1 << 15) fitNum |= IFIT_I16; |
if (relSize <= (uint64_t)1 << 31) fitNum |= IFIT_I32; |
code.fitNum = fitNum; |
code.sizeUnknown = uncertain; |
return uncertain; |
} |
if (pass < 4) { |
code.fitNum = IFIT_I8 | IFIT_I16 | IFIT_I32; // symbol values are not available yet |
code.sizeUnknown = 1; |
return 1; |
} |
// self-relative address to local symbol |
value = int32_t((uint32_t)symbols[isym3].st_value - (code.address + code.size * 4)); |
valueScaled = value + code.offset_mem; |
if (valueScaled >= -(1 << 7) && valueScaled < (1 << 7)) fitNum |= IFIT_I8; |
if (valueScaled >= -(1 << 15) && valueScaled < (1 << 15)) fitNum |= IFIT_I16; |
if (valueScaled >= -((int64_t)1 << 31) && valueScaled < ((int64_t)1 << 31)) fitNum |= IFIT_I32; |
code.fitNum = fitNum; |
// check if value is certain. uncertainty is stored in high part of st_value and sh_link |
uncertainty = int32_t((symbols[isym3].st_value >> 32) - sectionHeaders[code.section].sh_link); |
valueScaled += uncertainty; |
if ((valueScaled < -(1 << 7) || valueScaled >= (1 << 7)) && (fitNum & IFIT_I8)) uncertain |= 1; |
if ((valueScaled < -(1 << 15) || valueScaled >= (1 << 15)) && (fitNum & IFIT_I16)) uncertain |= 1; |
if ((valueScaled < -((int64_t)1 << 31) || valueScaled >= ((int64_t)1 << 31)) && (fitNum & IFIT_I32)) uncertain |= 1; |
if (uncertain && (code.fitNum & IFIT_LARGE)) { |
// choose the larger version if optimization process has convergence problems |
fitNum = (fitNum & (fitNum - 1)) | IFIT_I32; // remove the lowest set bit |
uncertain &= ~1; |
} |
code.fitNum = fitNum; |
code.sizeUnknown = uncertain; |
return uncertain; |
} |
|
// symbol is relative to data pointer or external constant. relocation needed |
fitNum = IFIT_RELOC; |
if (relSize <= 1 << 7) fitNum |= IFIT_I8; |
if (relSize <= 1 << 15) fitNum |= IFIT_I16; |
if (relSize <= (uint64_t)1 << 31) fitNum |= IFIT_I32; |
code.fitNum = fitNum; |
code.sizeUnknown = uncertain; |
return uncertain; |
} |
// no symbol. only a constant |
if (floatType) { |
// floating point constant |
code.fitNum = fitFloat(dvalue); |
if (uint8_t(code.dtype) < uint8_t(TYP_FLOAT64)) code.fitNum |= FFIT_32; |
code.sizeUnknown = 0; |
return 0; |
} |
// integer constant |
uint32_t low; // index of lowest set bit |
uint32_t high; // index of highest set bit |
fitNum = 0; |
int nbits; |
if (value == int64_t(0x8000000000000000)) { // prevent overflow of -value |
fitNum = 0; |
} |
else if (value >= 0) { |
low = bitScanForward((uint64_t)value); // lowest set bit |
high = bitScanReverse((uint64_t)value); // highest set bit |
//if (value < 8) fitNum |= IFIT_I4; |
//if (value == 8) fitNum |= IFIT_J4; |
//if (value < 0x10) fitNum |= IFIT_U4; |
if (value < 0x80) fitNum |= IFIT_I8 | IFIT_I8SHIFT; |
if (value == 0x80) fitNum |= IFIT_J8; |
if (value <= 0xFF) fitNum |= IFIT_U8; |
if (value < 0x8000) fitNum |= IFIT_I16 | IFIT_I16SH16; |
if (value == 0x8000) fitNum |= IFIT_J16; |
if (value <= 0xFFFF) fitNum |= IFIT_U16; |
if (high < 31) fitNum |= IFIT_I32; |
if (high < 32) fitNum |= IFIT_U32; |
if (value == 0x80000000U) fitNum |= IFIT_J32; |
nbits = high - low + 1; |
if (nbits < 8) fitNum |= IFIT_I8SHIFT; |
if (nbits < 16) { |
fitNum |= IFIT_I16SHIFT; |
if (low >= 16 && high < 31) fitNum |= IFIT_I16SH16; |
} |
if (nbits < 32) fitNum |= IFIT_I32SHIFT; |
if (low >= 32) fitNum |= IFIT_I32SH32; |
} |
else { // x < 0 |
value = -value; |
low = bitScanForward(value); // lowest set bit |
high = bitScanReverse(value); // highest set bit |
//if (value <= 8) fitNum |= IFIT_I4; |
if (value <= 0x80) fitNum |= IFIT_I8 | IFIT_I8SHIFT; |
if (value <= 0x8000) fitNum |= IFIT_I16 |IFIT_I16SH16 ; |
if (value <= 0x80000000U) fitNum |= IFIT_I32; |
nbits = high - low + 1; |
if (nbits < 8) fitNum |= IFIT_I8SHIFT; |
if (nbits < 16) { |
fitNum |= IFIT_I16SHIFT; |
if (low >= 16 && high <= 31) fitNum |= IFIT_I16SH16; |
} |
if (nbits < 32) fitNum |= IFIT_I32SHIFT; |
if (low >= 32) fitNum |= IFIT_I32SH32; |
} |
code.fitNum = fitNum; |
code.sizeUnknown = 0; |
return 0; |
} |
|
|
// Check how many bits are needed to a relative address or jump offset of an instruction. |
// This result is returned as bit-flags in codefitAddr, code.fitJump, and code.fitNum |
// The return value is nonzero if the size cannot be resolved yet. |
int CAssembler::fitAddress(SCode & code) { |
int64_t value = 0; // the constant or address to fit |
int64_t valueScaled; // value divided by scale factor |
uint32_t fitBits = 0; // bit flags indicating fit |
int32_t isym1 = 0, isym2 = 0; // symbol index |
int32_t uncertainty; // maximum deviance if the value is uncertain |
int uncertain = 0; // return value |
|
if (code.instruction == II_ALIGN) return 0; // not an instruction |
if (!(code.etype & (XPR_OFFSET | XPR_JUMPOS | XPR_MEM))) return 0; // has no address |
|
// check address of memory operand |
if (code.sym1) { |
// there is a memory operand symbol |
code.etype |= XPR_OFFSET; |
|
value = code.offset_mem; // memory offset |
isym1 = findSymbol(code.sym1); |
if (isym1 <= 0) { |
code.sizeUnknown = 2; return 2; // should not occur |
} |
if (symbols[isym1].st_unitsize == 0) uncertain = 2; // symbol value is not known yet |
uint32_t sym1section = symbols[isym1].st_section; // symbol section |
if (sym1section < sectionHeaders.numEntries()) { |
// determine necessary relocation size if relocation needed |
uint64_t relSize; // maximum size of relocated address |
if (symbols[isym1].st_type == STT_CONSTANT) { |
// assume that constant offset is limited by dataSizeOption |
relSize = data_size; // relocation size for code and constant data |
} |
else if (sym1section |
&& !(sectionHeaders[sym1section].sh_flags & (SHF_WRITE | SHF_DATAP | SHF_THREADP))) { |
relSize = code_size; // relocation size for code and constant data |
} |
else if (sym1section) { // local symbol with known section |
relSize = (sectionHeaders[sym1section].sh_flags & (SHF_EXEC | SHF_IP)) ? code_size : data_size; |
} |
else { // external symbol with unknown section. look at symbol attributes |
relSize = (symbols[isym1].st_other & (STV_EXEC | STV_IP)) ? code_size : data_size; |
} |
if (code.sym2) { |
// value is (sym1 - sym2) / scale factor |
isym2 = findSymbol(code.sym2); |
if (isym2 <= 0) { |
code.sizeUnknown = 2; return 2; // should not occur |
} |
if (symbols[isym1].st_unitsize == 0) uncertain = 2; // symbol value is not known yet |
if (symbols[isym1].st_section != symbols[isym2].st_section || symbols[isym1].st_bind != STB_LOCAL || symbols[isym2].st_bind != STB_LOCAL) { |
// different sections or not local. relocation needed |
fitBits = IFIT_RELOC; |
if (code.symscale1 > 1) relSize /= code.symscale1; // value is scaled |
if (relSize <= 1 << 7) fitBits |= IFIT_I8; |
if (relSize <= 1 << 15) fitBits |= IFIT_I16; |
//if (relSize <= 1 << 23) fitBits |= IFIT_I24; |
if (relSize <= (uint64_t)1 << 31) fitBits |= IFIT_I32; |
code.fitAddr = fitBits; |
code.sizeUnknown += uncertain; |
//return uncertain; |
} |
// difference between two local symbols |
else if (pass < 4) { |
code.fitAddr = IFIT_I8 | IFIT_I16 | IFIT_I32; // symbol values are not available yet |
code.sizeUnknown += 1; |
uncertain += 1; |
//return 1; |
} |
else { |
value += int32_t(uint32_t(symbols[isym1].st_value) - uint32_t(symbols[isym2].st_value)); |
int scale = code.symscale1; |
if (scale < 1) scale = 1; |
valueScaled = value / scale + code.offset_mem; |
if (valueScaled >= -(1 << 7) && valueScaled < (1 << 7)) fitBits |= IFIT_I8; |
if (valueScaled >= -(1 << 15) && valueScaled < (1 << 15)) fitBits |= IFIT_I16; |
if (valueScaled >= -((int64_t)1 << 31) && valueScaled < ((int64_t)1 << 31)) fitBits |= IFIT_I32; |
// check if value is certain. uncertainty is stored in high part of st_value |
uncertainty = (symbols[isym1].st_value >> 32) - (symbols[isym2].st_value >> 32); |
valueScaled = value / scale + code.offset_mem + uncertainty; |
if (code.symscale1 > 1) valueScaled /= code.symscale1; // value is scaled |
if ((valueScaled < -(1 << 7) || valueScaled >= (1 << 7)) && (fitBits & IFIT_I8)) uncertain |= 1; |
if ((valueScaled < -(1 << 15) || valueScaled >= (1 << 15)) && (fitBits & IFIT_I16)) uncertain |= 1; |
if ((valueScaled < -((int64_t)1 << 31) || valueScaled >= ((int64_t)1 << 31)) && (fitBits & IFIT_I32)) uncertain |= 1; |
if (uncertain && (code.fitAddr & IFIT_LARGE)) { |
// choose the larger version if optimization process has convergence problems |
fitBits = (fitBits & (fitBits - 1)) | IFIT_I32; // remove the lowest set bit |
uncertain &= ~1; |
} |
code.fitAddr = fitBits; |
code.sizeUnknown += uncertain; |
//return uncertain; |
} |
} |
// one symbol |
else if (sectionHeaders[sym1section].sh_flags & SHF_IP) { |
// relative to instruction pointer |
if (sym1section != code.section || symbols[isym1].st_bind != STB_LOCAL) { |
// symbol is in different section or not local. relocation needed |
fitBits = IFIT_RELOC; |
if (code.etype & XPR_JUMPOS) relSize >>= 2; // value is scaled by 4 |
if (relSize <= 1 << 7) fitBits |= IFIT_I8; // necessary relocation size |
if (relSize <= 1 << 15) fitBits |= IFIT_I16; |
if (relSize <= 1 << 23) fitBits |= IFIT_I24; |
if (relSize <= (uint64_t)1 << 31) fitBits |= IFIT_I32; |
code.fitAddr = fitBits; |
code.sizeUnknown += uncertain; |
//return uncertain; |
} |
else if (pass < 4) { |
// code.fitBits = IFIT_I16 | IFIT_I32; // symbol values are not available yet |
code.fitAddr = IFIT_I16 | IFIT_I24 | IFIT_I32; // symbol values are not available yet |
code.sizeUnknown += 1; |
uncertain |= 1; |
//return 1; |
} |
else { // self-relative address to local symbol |
value = int32_t((uint32_t)symbols[isym1].st_value - (code.address + code.size * 4)); |
valueScaled = value; |
valueScaled += code.offset_mem; |
if (valueScaled >= -(1 << 15) && valueScaled < (1 << 15)) fitBits |= IFIT_I16; |
if (valueScaled >= -(1 << 23) && valueScaled < (1 << 23)) fitBits |= IFIT_I24; |
if (valueScaled >= -((int64_t)1 << 31) && valueScaled < ((int64_t)1 << 31)) fitBits |= IFIT_I32; |
code.fitAddr = fitBits; |
// check if value is certain. uncertainty is stored in high part of st_value and sh_link |
uncertainty = int32_t((symbols[isym1].st_value >> 32) - sectionHeaders[code.section].sh_link); |
valueScaled += uncertainty; |
if ((valueScaled < -(1 << 7) || valueScaled >= (1 << 7)) && (fitBits & IFIT_I8)) uncertain |= 1; |
if ((valueScaled < -(1 << 15) || valueScaled >= (1 << 15)) && (fitBits & IFIT_I16)) uncertain |= 1; |
if ((valueScaled < -(1 << 23) || valueScaled >= (1 << 23)) && (fitBits & IFIT_I24)) uncertain |= 1; |
if ((valueScaled < -((int64_t)1 << 31) || valueScaled >= ((int64_t)1 << 31)) && (fitBits & IFIT_I32)) uncertain |= 1; |
if (uncertain && (code.fitAddr & IFIT_LARGE)) { |
// choose the larger version if optimization process has convergence problems |
fitBits = (fitBits & (fitBits - 1)) | IFIT_I32; // remove the lowest set bit |
uncertain &= ~1; |
} |
code.fitAddr = fitBits; |
code.sizeUnknown += uncertain; |
//return uncertain; |
} |
} |
else { |
// symbol is relative to data pointer. relocation needed |
fitBits = IFIT_RELOC; |
if (relSize <= 1 << 7) fitBits |= IFIT_I8; |
if (relSize <= 1 << 15) fitBits |= IFIT_I16; |
if (relSize <= (uint64_t)1 << 31) fitBits |= IFIT_I32; |
code.fitAddr = fitBits; |
code.sizeUnknown += uncertain; |
} |
} |
} |
else { |
// no symbol. only a signed integer constant |
value = code.offset_mem; |
fitBits = 0; |
if (value >= -(int64_t)0x80 && value < 0x80) fitBits |= IFIT_I8; |
if (value >= -(int64_t)0x8000 && value < 0x8000) fitBits |= IFIT_I16; |
if (value >= -(int64_t)0x80000000 && value < 0x80000000) fitBits |= IFIT_I32; |
code.fitAddr = fitBits; |
} |
|
// check jump offset symbol |
if (code.sym5) { |
// there is a jump offset symbol |
value = code.offset_jump; // jump offset |
fitBits = 0; |
|
isym1 = findSymbol(code.sym5); |
if (isym1 <= 0) { |
code.sizeUnknown = 2; return 2; // should not occur |
} |
// one symbol relative to instruction pointer |
if (symbols[isym1].st_unitsize == 0) uncertain = 2; // symbol value is not known yet |
uint32_t sym1section = symbols[isym1].st_section; // symbol section |
if (sym1section < sectionHeaders.numEntries()) { |
// determine necessary relocation size if relocation needed |
uint64_t relSize; // maximum size of relocated address |
relSize = code_size >> 2; // relocation size for code and constant data, scaled by 4 |
|
if (sym1section != code.section || symbols[isym1].st_bind != STB_LOCAL) { |
// symbol is in different section or not local. relocation needed |
fitBits = IFIT_RELOC; |
if (relSize <= 1 << 7) fitBits |= IFIT_I8; // necessary relocation size |
if (relSize <= 1 << 15) fitBits |= IFIT_I16; |
if (relSize <= 1 << 23) fitBits |= IFIT_I24; |
if (relSize <= (uint64_t)1 << 31) fitBits |= IFIT_I32; |
code.fitJump = fitBits; |
code.sizeUnknown += uncertain; |
//return uncertain; |
} |
else if (pass < 4) { |
code.fitJump = IFIT_I16 | IFIT_I24 | IFIT_I32; // symbol values are not available yet |
code.sizeUnknown += 1; |
uncertain = 1; |
//return 1; |
} |
else { |
// self-relative address to local symbol |
value = int32_t((uint32_t)symbols[isym1].st_value - (code.address + code.size * 4)); |
valueScaled = value >> 2; // jump address is scaled |
valueScaled += code.offset_jump; |
if (valueScaled >= -(1 << 7) && valueScaled < (1 << 7)) fitBits |= IFIT_I8; |
if (valueScaled >= -(1 << 15) && valueScaled < (1 << 15)) fitBits |= IFIT_I16; |
if (valueScaled >= -(1 << 23) && valueScaled < (1 << 23)) fitBits |= IFIT_I24; |
if (valueScaled >= -((int64_t)1 << 31) && valueScaled < ((int64_t)1 << 31)) fitBits |= IFIT_I32; |
code.fitJump = fitBits; |
// check if value is certain. uncertainty is stored in high part of st_value and sh_link |
uncertainty = int32_t((symbols[isym1].st_value >> 32) - sectionHeaders[code.section].sh_link); |
valueScaled += uncertainty; |
if ((valueScaled < -(1 << 7) || valueScaled >= (1 << 7)) && (fitBits & IFIT_I8)) uncertain |= 1; |
if ((valueScaled < -(1 << 15) || valueScaled >= (1 << 15)) && (fitBits & IFIT_I16)) uncertain |= 1; |
if ((valueScaled < -(1 << 23) || valueScaled >= (1 << 23)) && (fitBits & IFIT_I24)) uncertain |= 1; |
if ((valueScaled < -((int64_t)1 << 31) || valueScaled >= ((int64_t)1 << 31)) && (fitBits & IFIT_I32)) uncertain |= 1; |
if (uncertain && (code.fitAddr & IFIT_LARGE)) { |
// choose the larger version if optimization process has convergence problems |
fitBits = (fitBits & (fitBits - 1)) | IFIT_I32; // remove the lowest set bit |
uncertain &= ~1; |
code.fitJump = fitBits; |
//code.sizeUnknown += uncertain; |
} |
code.sizeUnknown += uncertain; |
} |
} |
} |
return uncertain; |
} |
|
|
// find format details in formatList from entry in instructionlist |
uint32_t findFormat(SInstruction const & listentry, uint32_t imm) { |
// listentry: record in instructionlist or instructionlistId |
// imm: immediate operand, if any |
|
// make model instruction for lookupFormat |
STemplate instrModel; |
instrModel.a.il = listentry.format >> 8; |
instrModel.a.mode = (listentry.format >> 4) & 7; |
instrModel.a.ot = (listentry.format >> 5) & 4; |
if ((listentry.format & ~ 0x12F) == 0x200) { // format 0x200, 0x220, 0x300, 0x320 |
instrModel.a.mode2 = listentry.format & 7; |
} |
else if ((listentry.format & 0xFF0) == 0x270 && listentry.op1 < 8) { |
instrModel.a.mode2 = listentry.op1 & 7; |
} |
else instrModel.a.mode2 = 0; |
instrModel.a.op1 = listentry.op1; |
instrModel.b[0] = imm & 0xFF; |
// look op details for this format (from emulator2.cpp) |
return lookupFormat(instrModel.q); |
} |
|
// find the smallest representation that the floating point operand fits into |
int fitFloat(double x) { |
if (x == 0.) return IFIT_I8 | FFIT_16 | FFIT_32 | FFIT_64; |
union { |
double d; |
struct { |
uint64_t mantissa: 52; |
uint64_t exponent: 11; |
uint64_t sign: 1; |
} f; |
} u; |
u.d = x; |
int fit = FFIT_64; |
// check if mantissa fits |
if ((u.f.mantissa & (((uint64_t)1 << 42) - 1)) == 0) fit |= FFIT_16; |
if ((u.f.mantissa & (((uint64_t)1 << 29) - 1)) == 0) fit |= FFIT_32; |
// check if exponent fits, except for infinity or nan |
if (u.f.exponent != 0x7FF) { |
int ex = int(u.f.exponent - 0x3FF); |
if (ex < -14 || ex > 15) fit &= ~FFIT_16; |
if (ex < -126 || ex > 127) fit &= ~FFIT_32; |
} |
// check if x fits into a small integer |
if (fit & FFIT_16) { |
int i = int(x); |
if (i == x && i >= -128 && i < 128) { |
fit |= IFIT_I8; |
} |
} |
return fit; |
} |
|
// find an instruction variant that fits the code |
int CAssembler::fitCode(SCode & code) { |
// return value: |
// 0: does not fit |
// 1: fits |
uint32_t bestInstr = 0; // best fitting instruction variant, index into instructionlistId |
uint32_t bestSize = 99; // size of best fitting instruction variant |
SCode codeTemp; // fitted code |
SCode codeBest; // best fitted code |
uint32_t instrIndex = 0, ii; // index into instructionlistId |
uint32_t formatIx = 0; // index into formatList |
uint32_t isize; // il bits |
codeBest.category = 0; |
|
// find instruction by id |
SInstruction3 sinstr; // make dummy record with instruction id as parameter to findAll |
if (code.instruction == II_ALIGN) { |
return 1; // alignment directive |
} |
sinstr.id = code.instruction; |
int32_t nInstr = instructionlistId.findAll(&instrIndex, sinstr); |
|
if (code.etype & (XPR_IMMEDIATE | XPR_OFFSET | XPR_LIMIT | XPR_JUMPOS)) { |
// there is an immediate constant, offset, or limit. |
// generate specific error message if large constant cannot fit |
if ((code.etype & XPR_OFFSET) && !(code.etype & XPR_IMMEDIATE) && !(code.fitAddr & IFIT_I32)) { |
errors.reportLine(ERR_OFFSET_TOO_LARGE); |
} |
//else if ((code.etype & XPR_LIMIT) && !(code.fitBits & (IFIT_U16 | IFIT_U32))) errors.reportLine(ERR_LIMIT_TOO_LARGE); |
else if ((code.etype & XPR_IMMEDIATE) && !(code.etype & XPR_INT2)) { |
if (!(code.fitNum & (IFIT_I16 | IFIT_I16SHIFT | IFIT_I32 | IFIT_I32SHIFT | FFIT_16 | FFIT_32)) && (code.etype & XPR_OPTIONS) && code.optionbits) { |
errors.reportLine(ERR_IMMEDIATE_TOO_LARGE); |
} |
} |
} |
if (lineError) return 0; |
|
// loop through all instruction definitions with same id |
for (ii = instrIndex; ii < instrIndex + nInstr; ii++) { |
// category |
code.instr1 = ii; |
code.category = instructionlistId[ii].category; |
// get variant bits from instruction list |
variant = instructionlistId[ii].variant; // instruction-specific variants |
|
switch (instructionlistId[ii].category) { |
case 1: // single format. find entry in formatList |
formatIx = findFormat(instructionlistId[ii], code.value.w); |
code.formatp = formatList + formatIx; |
if (instructionFits(code, codeTemp, ii)) { |
// check if smaller than previously found. |
isize = codeTemp.size; |
if (isize < bestSize) { |
bestSize = isize; |
bestInstr = ii; |
codeBest = codeTemp; |
} |
} |
break; |
|
case 3: // multi-format instructions. search all formats for the best one |
for (formatIx = 0; formatIx < formatList3.numEntries(); formatIx++) { |
code.formatp = &formatList3[formatIx]; |
|
if (((uint64_t)1 << code.formatp->formatIndex) & instructionlistId[ii].format) { |
if (instructionFits(code, codeTemp, ii)) { |
// check if smaller than previously found. category 3 = multiformat preferred |
isize = codeTemp.size; |
if (isize < bestSize || (isize == bestSize && codeBest.category != 3)) { |
bestSize = isize; |
bestInstr = ii; |
codeBest = codeTemp; |
} |
} |
} |
} |
break; |
|
case 4: // jump instructions. search all formats for the best one |
for (formatIx = 0; formatIx < formatList4.numEntries(); formatIx++) { |
code.formatp = &formatList4[formatIx]; |
if (((uint64_t)1 << code.formatp->formatIndex) & instructionlistId[ii].format) { |
if (jumpInstructionFits(code, codeTemp, ii)) { |
// check if smaller than previously found. category 3 = multiformat preferred |
isize = codeTemp.size; |
if (isize < bestSize) { |
bestSize = isize; |
bestInstr = ii; |
codeBest = codeTemp; |
} |
} |
} |
} |
break; |
|
default: |
return 0; // error in list |
} |
} |
|
if (bestSize > 4) { |
errors.reportLine(checkCodeE(code)); // find reason why no format fits, and report error |
return 0; |
} |
|
code = codeBest; // get the best fitting code |
variant = instructionlistId[bestInstr].variant; // instruction-specific variants |
|
checkCode2(code); // check if operands are correct |
|
if (lineError) return 0; |
return 1; |
} |
|
|
// check if instruction fits into specified format |
bool CAssembler::instructionFits(SCode const & code, SCode & codeTemp, uint32_t ii) { |
// code: structure defining all operands and options |
// codeTemp: fitted code |
// ii: index into instructionlistId |
// formatIndex: index into formatList |
|
uint32_t shiftCount; // shift count for shifted constant |
// copy code structure and add details |
codeTemp = code; |
codeTemp.category = code.formatp->category; |
codeTemp.size = (code.formatp->format2 >> 8) & 3; |
if (codeTemp.size == 0) codeTemp.size = 1; |
codeTemp.instr1 = ii; |
|
if (instructionlistId[ii].opimmediate == OPI_IMPLICIT && !(code.etype & XPR_IMMEDIATE)) { |
// There is no immediate operand. instructionlistId[ii] has an implicit immediate operand. |
// Insert implicit operand and see if it fits |
codeTemp.value.u = instructionlistId[ii].implicit_imm; |
codeTemp.etype |= XPR_INT; |
codeTemp.fitNum = 0xFFFFFFFF; |
} |
|
// check vector use |
bool useVectors = (code.dtype & TYP_FLOAT) |
|| (code.dest & 0xE0) == REG_V |
|| (code.reg1 & 0xE0) == REG_V |
|| (code.reg2 & 0xE0) == REG_V; |
|
if (useVectors) { |
if (!(code.formatp->vect)) return false; // vectors not supported |
} |
else if (code.formatp->vect & ~0x10) return false; // vectors provided but not used |
|
// requested operand type |
uint32_t requestOT = code.dtype & 7; |
if (uint8_t(code.dtype) == uint8_t(TYP_FLOAT16)) { |
requestOT = TYP_INT16 & 7; // replace pseudo-type TYP_FLOAT16 with TYP_INT16 |
codeTemp.dtype = TYP_INT16; |
} |
|
// operand type provided by this format |
uint32_t formatOT = code.formatp->ot; |
if (formatOT == 0x32) formatOT = 0x12 + (instructionlistId[ii].op1 & 1); // int32 for even op1, int64 for odd op1 |
if (formatOT == 0x35) formatOT = 0x15 + (instructionlistId[ii].op1 & 1); // float for even op1, double for odd op1 |
if (formatOT == 0) formatOT = requestOT; // operand type determined by OT field |
formatOT &= 7; |
uint32_t scale2 = formatOT; |
if (scale2 > 4) scale2 -= 3; // operand size = 1 << scale2 |
|
if (variant & (VARIANT_D0 | VARIANT_D2)) { // no operand type |
if (code.dtype == 0 && code.instruction != II_NOP) codeTemp.dtype = formatOT ? formatOT : 3; |
} |
else { |
// check requested operand type |
if (formatOT <= 3 && requestOT < formatOT && (code.dtype & TYP_PLUS)) { |
requestOT = formatOT; // request allows bigger type |
// codeTemp.dtype = formatOT; // prevents merging with subsequent jump with smaller type than formatOT |
} |
if (requestOT != formatOT && code.dtype) return false; // requested format type not supported |
|
// check if operand type supported by instruction |
uint32_t optypessupport = useVectors ? (instructionlistId[ii].optypesscalar | instructionlistId[ii].optypesvector) : instructionlistId[ii].optypesgp; |
optypessupport |= optypessupport >> 8; // include types with optional support |
if (!(optypessupport & (1 << requestOT))) return false; |
} |
|
// check if there are enough register operands in this format |
uint8_t opAvail = code.formatp->opAvail; |
uint8_t numReg = ((opAvail >> 4) & 1) + ((opAvail >> 5) & 1) + ((opAvail >> 6) & 1) + ((opAvail >> 7) & 1); // number of registers available |
uint8_t numReq = instructionlistId[ii].sourceoperands; // number of registers required for this instruction |
codeTemp.numOp = numReq; |
if ((codeTemp.etype & XPR_IMMEDIATE) && numReq) numReq--; |
if ((codeTemp.etype & XPR_MEM) && numReq) numReq--; |
if ((codeTemp.etype & (XPR_MASK | XPR_FALLBACK)) && ((code.fallback & 0x1F) != (code.reg1 & 0x1F) || (code.reg1 & 0x1F) == 0x1F)) { |
numReq += 2; // fallback different from reg1, implies reg1 != destination |
} |
else if ((code.etype & XPR_REG1) && code.dest && code.reg1 != code.dest && !(variant & VARIANT_D3)) { |
numReq++; // reg1 != destination |
} |
if (numReq > numReg) return false; // not enough registers in this format |
|
// check if mask available |
if ((code.etype & XPR_MASK) && !(code.formatp->tmplate == 0xA || code.formatp->tmplate == 0xE)) return false; |
|
// check option bits |
if ((code.etype & XPR_OPTIONS) && code.optionbits != 0 |
&& (code.formatp->tmplate != 0xE || !(code.formatp->imm2 & 2)) |
&& (variant & VARIANT_On) && instructionlistId[ii].opimmediate != OPI_INT1688) return false; // only template E has option bits |
|
// check memory operand |
if (code.etype & XPR_MEM) { |
if (code.formatp->mem == 0) return false; // memory operand requested but not supported |
if (code.etype & XPR_SYM1) { // has data symbol |
if (code.etype & XPR_SYM2) { // has difference between two symbols |
codeTemp.sizeUnknown = 1; |
} |
//if (!(code.fitNumX & IFIT_I32)) return false; // assume symbol address requires 32 bits. local symbol difference resolved later when sizeUnknown = 1 |
} |
// check index and scale factor |
if (code.etype & XPR_INDEX) { |
if (!(code.formatp->mem & 4)) return false; // index not supported |
if ((code.formatp->scale & 4) && code.scale != -1) return false; // scale factor must be -1 |
if ((code.formatp->scale & 2) && code.scale != 1 << scale2) return false; // scale factor must match operand type |
if (!(code.formatp->scale & 6) && code.scale != 1) return false; // scale factor must be 1 |
} |
else { // no index requested |
if (code.formatp->mem & 4) { |
codeTemp.index = 0x1F; // RT = 0x1F means no index |
codeTemp.scale = 1 << scale2; |
} |
} |
|
// check address offset size |
if (code.etype & (XPR_OFFSET | XPR_SYM1)) { |
if (!(code.formatp->mem & 0x10)) return false; // format does not support memory offset |
switch (code.formatp->addrSize) { |
case 1: |
if (code.sym1 && !(code.fitAddr & IFIT_I8)) return false; |
if ((code.base & 0x1F) >= 0x1C && (code.base & 0x1F) != 0x1F) return false; // ip, datap, threadp must have 16 bit offset |
// no relocation. scale factor depends on operand size |
if (code.offset_mem & ((1 << scale2) - 1)) return false; // offset is not a multiple of the scale factor |
if ((code.offset_mem >> scale2) < -0x80 || (code.offset_mem >> scale2) > 0x7F) return false; |
break; |
case 2: |
if (!(code.fitAddr & IFIT_I16)) return false; |
break; |
case 4: |
if (!(code.fitAddr & IFIT_I32)) return false; |
break; |
default: |
return false; |
} |
} |
else if ((code.formatp->addrSize) < 2 && (code.base & 0x1F) >= 0x1C && (code.base & 0x1F) != 0x1F) return false; |
|
// fail if limit required and not supported, or supported and not required |
if (code.etype & XPR_LIMIT) { |
if (!(code.formatp->mem & 0x20)) return false; // limit not supported by format |
switch (code.formatp->addrSize) { |
case 1: if (code.value.u >= 0x100) return false; |
break; |
case 2: if (code.value.u >= 0x10000) return false; |
break; |
case 4: if (uint64_t(code.value.u) >= 0x100000000U) return false; |
break; |
} |
} |
else { |
if (code.formatp->mem & 0x20) return false; // limit provided but not requested |
} |
|
// check length/broadcast/scalar |
if (code.etype & XPR_SCALAR) { // scalar operand requested |
if ((code.formatp->vect & 6) != 0) { |
codeTemp.length = 31; // disable length or broadcast option |
} |
} |
else if (code.etype & XPR_LENGTH) { // vector length specified |
if ((code.formatp->vect & 2) == 0) return false; // vector length not in this format |
} |
else if (code.etype & XPR_BROADC) { // vector broadcast specified |
if ((code.formatp->vect & 4) == 0) return false; // vector broadcasst not in this format |
} |
} |
else if (code.formatp->mem) return false; // memory operand supported by not requested |
|
// check immediate operand |
//bool isFloat = (code.dtype & TYP_FLOAT32 & 0xF0) != 0; // specified type is float or double or float128 |
bool hasImmediate = (code.etype & XPR_IMMEDIATE) != 0; // && !(code.etype & (XPR_OFFSET | XPR_LIMIT))); |
|
/*if ((variant & VARIANT_M1) && code.formatp->mem && code.formatp->tmplate == 0xE) { |
// variant M1: immediate operand is in IM3. No further check needed |
// to do: fail if relocation on immediate |
return hasImmediate; // succeed if there is an immediate |
} */ |
|
if (hasImmediate) { |
if (code.formatp->immSize == 0 && instructionlistId[ii].sourceoperands < 4) return false; // immediate not supported |
|
// to do: check if relocation |
|
// check if size fits. special cases in instruction list |
switch (instructionlistId[ii].opimmediate) { |
case OPI_IMPLICIT: // implicit value of immediate operand. Accept explicit value only if same |
if (codeTemp.value.u != instructionlistId[ii].implicit_imm) return false; |
break; |
|
case OPI_INT8SH: // im2 << im1 |
if (code.fitNum & (IFIT_I8 | IFIT_I8SHIFT)) { // fits im2 << im1 |
shiftCount = bitScanForward(codeTemp.value.u); |
codeTemp.value.u = (codeTemp.value.u >> shiftCount << 8) | shiftCount; |
codeTemp.fitNum |= IFIT_I16; // make it accepted below |
break; |
} |
return false; |
case OPI_INT16SH16: // im12 << 16 |
if (code.fitNum & (IFIT_I16 | IFIT_I16SH16)) { // fits im2 << 16 |
codeTemp.value.u = codeTemp.value.u >> 16; |
codeTemp.fitNum |= IFIT_I16; // make it accepted below |
break; |
} |
return false; |
case OPI_INT32SH32: // im2 << 32 |
if (code.fitNum & (IFIT_I32 | IFIT_I32SH32)) { // fits im2 << 32 |
codeTemp.value.u = codeTemp.value.u >> 32; |
codeTemp.fitNum |= IFIT_I32; // make it accepted below |
break; |
} |
return false; |
case OPI_UINT8: // 8 bit unsigned integer |
if (value0 < 0x100 && value0 > -(int64_t)0x80U) return true; |
return false; |
case OPI_UINT16: // 16 bit unsigned integer |
if (value0 < 0x10000 && value0 > -(int64_t)0x8000U) return true; |
return false; |
case OPI_UINT32: // 32 bit unsigned integer |
//if (code.fitNum & IFIT_U32) return true; // this does not work if a float type is specified |
if (value0 < 0x100000000 && value0 > -(int64_t)0x80000000U) return true; |
return false; |
case OPI_INT886: // three integers |
codeTemp.value.u = (codeTemp.value.w & 0xFF) | (codeTemp.value.u >> 24); |
return true; |
case OPI_INT1688: // three integers: 16 + 8 + 8 bits |
codeTemp.value.u = (codeTemp.value.w & 0xFFFF) | (codeTemp.value.u >> 16 & 0xFF0000) | codeTemp.optionbits << 24; |
return true; |
case OPI_OT: // constant of same type as operand type |
if ((uint8_t(code.dtype) & ~TYP_UNS) <= uint8_t(TYP_INT32) && code.formatp->immSize >= 4) return true; |
} |
// check if size fits. general cases |
switch (code.formatp->immSize) { |
case 1: |
if (codeTemp.fitNum & IFIT_I8) break; // fits |
if ((variant & VARIANT_U0) && (codeTemp.fitNum & IFIT_U8)) break; // unsigned fits |
if ((codeTemp.dtype & 0x1F) == (TYP_INT8 & 0x1F) && (codeTemp.fitNum & IFIT_U8)) break; // 8 bit size fits unsigned with no sign extension |
return false; |
case 2: |
if (codeTemp.fitNum & (IFIT_I16 | FFIT_16)) break; // fits |
if ((variant & VARIANT_U0) && (codeTemp.fitNum & IFIT_U16)) break; // unsigned fits |
if ((codeTemp.dtype & 0x1F) == (TYP_INT16 & 0x1F) && code.formatp->tmplate != 0xC && (codeTemp.fitNum & IFIT_U16)) break; // 16 bit size fits unsigned with no sign extension |
if ((code.formatp->imm2 & 4) && !(variant & VARIANT_On) && (codeTemp.fitNum & IFIT_I16SHIFT)) { |
// fits with im2 << im3 |
shiftCount = bitScanForward(codeTemp.value.u); |
codeTemp.value.u >>= shiftCount; |
codeTemp.optionbits = shiftCount; |
break; |
} |
if (variant & VARIANT_H0) break; // half precision fits |
return false; |
case 4: |
if ((code.dtype & 0xFF) == (TYP_FLOAT32 & 0xFF)) break; // float32 must be rounded to fit |
if (codeTemp.fitNum & (IFIT_I32 | FFIT_32)) break; // fits |
if ((codeTemp.fitNum & IFIT_U32) && (code.dtype & 0xFF) == (TYP_INT32 & 0xFF)) break; // fits |
if ((variant & VARIANT_U0) && (codeTemp.fitNum & IFIT_U32)) break; // unsigned fits |
if (variant & VARIANT_H0) break; // half precision fits |
if ((codeTemp.dtype & 0x1F) == (TYP_INT32 & 0x1F) && (codeTemp.fitNum & IFIT_U32)) break; // 32 bit size fits unsigned with no sign extension |
if ((code.formatp->imm2 & 8) && (codeTemp.fitNum & IFIT_I32SHIFT)) { |
// fits with im4 << im2 |
shiftCount = bitScanForward(codeTemp.value.u); |
codeTemp.value.u = ((codeTemp.value.u >> shiftCount) & 0xFFFFFFFF) | ((uint64_t)shiftCount << 32); // store shift count in upper half |
break; |
} |
return false; |
case 8: |
break; |
default:; // other values should not occur in table |
} |
} |
else if ((code.formatp->immSize != 0) && !(code.etype & (XPR_OFFSET | XPR_LIMIT)) |
&& instructionlistId[ii].sourceoperands && code.category != 1) { |
return false; // immediate operand provided but not required |
} |
return true; |
} |
|
// check if instruction fits into specified format |
bool CAssembler::jumpInstructionFits(SCode const & code, SCode & codeTemp, uint32_t ii) { |
// code: structure defining all operands and options |
// codeTemp: fitted code |
// ii: index into instructionlistId |
// formatIndex: index into formatList4 |
|
//uint8_t offsetSize = 0; // number of bytes to use in relative address |
//uint8_t immediateSize = 0; // number of bytes to use in immediate operand |
bool offsetRelocated = false; // relative offset needs relocation |
//bool immediateRelocated = false; // immediate operand needs relocation |
|
codeTemp = code; |
codeTemp.category = code.formatp->category; |
codeTemp.size = (code.formatp->format2 >> 8) & 3; |
codeTemp.instr1 = ii; |
|
// check vector use |
bool useVectors = (code.dtype & TYP_FLOAT) || (code.dest & 0xE0) == REG_V || (code.reg1 & 0xE0) == REG_V; |
if (useVectors) { |
if (!(code.formatp->vect)) return false; // vectors not supported |
} |
|
// operand type provided by this format |
uint32_t formatOT = code.formatp->ot; |
if (formatOT == 0) formatOT = code.dtype; // operand type determined by OT field |
formatOT &= 7; |
|
// check requested operand type |
uint32_t requestOT = code.dtype & 7; |
if (formatOT <= 3 && requestOT < formatOT && (code.dtype & TYP_PLUS)) { |
requestOT = formatOT; // request allows bigger type |
codeTemp.dtype = formatOT; |
} |
if (requestOT != formatOT && code.dtype) return false; // requested format type not supported |
|
// check if operand type supported by instruction |
uint32_t optypessupport = useVectors ? (instructionlistId[ii].optypesscalar | instructionlistId[ii].optypesvector) : instructionlistId[ii].optypesgp; |
optypessupport |= optypessupport >> 8; // include types with optional support |
if (!(optypessupport & (1 << requestOT))) return false; |
|
// check if there are enough register operands in this format |
uint8_t opAvail = code.formatp->opAvail; |
uint8_t numReg = ((opAvail >> 4) & 1) + ((opAvail >> 5) & 1) + ((opAvail >> 7) & 1); // number of registers available |
uint8_t numReq = instructionlistId[ii].sourceoperands; // number of registers required for this instruction |
if ((code.etype & XPR_REG1) && code.dest && code.reg1 != code.dest && numReq > 2) { |
numReq++; // reg1 != destination, except if no reg2 |
} |
if (code.formatp->jumpSize) numReq--; |
if ((code.etype & (XPR_IMMEDIATE | XPR_MEM)) && numReq) numReq--; |
if ((code.etype & XPR_INT2) && numReq) numReq--; |
if (numReq > numReg) return false; // not enough registers in this format |
|
// check if correct number of registers specified |
uint8_t nReg = 0; |
for (int j = 0; j < 3; j++) nReg += (code.etype & (XPR_REG1 << j)) != 0; |
if (code.dest && code.dest != code.reg1) nReg++; |
if (nReg != numReq) return false; |
|
// check if mask available |
if ((code.etype & XPR_MASK) && !(fInstr->tmplate == 0xA || fInstr->tmplate == 0xE)) return false; |
|
// self-relative jump offset |
if (code.etype & XPR_JUMPOS) { |
if (!(code.formatp->jumpSize)) return false; |
switch (code.formatp->jumpSize) { |
case 0: // no offset |
if (code.offset_jump || offsetRelocated) return false; |
break; |
case 1: // 1 byte |
if (!(code.fitJump & IFIT_I8)) return false; |
break; |
case 2: // 2 bytes |
if (!(code.fitJump & IFIT_I16)) return false; |
break; |
case 3: // 3 bytes |
if (!(code.fitJump & IFIT_I24)) return false; |
break; |
case 4: // 4 bytes |
if (!(code.fitJump & IFIT_I32)) return false; |
break; |
} |
} |
else { // no self-relative jump offset |
if (code.formatp->jumpSize) return false; |
} |
|
if (instructionlistId[ii].opimmediate == OPI_IMPLICIT && !(code.etype & XPR_IMMEDIATE)) { |
// There is no immediate operand. instructionlistId[ii] has an implicit immediate operand. |
// Insert implicit operand and see if it fits |
codeTemp.value.u = instructionlistId[ii].implicit_imm; |
codeTemp.etype |= XPR_INT; |
codeTemp.fitNum = 0xFFFFFFFF; |
} |
|
// immediate operand |
if (codeTemp.etype & XPR_IMMEDIATE) { |
if (code.dtype & TYP_FLOAT) { |
if (variant & VARIANT_I2) { |
// immediate should be integer |
codeTemp.etype = (code.etype & ~XPR_FLT) | XPR_INT; |
codeTemp.value.i = (int64_t)code.value.d; |
switch (code.formatp->immSize) { |
case 0: // no immediate |
return false; |
case 1: // 1 byte |
if (codeTemp.value.i < -0x80 || codeTemp.value.i > 0x7F) return false; |
break; |
case 2: // 2 bytes |
if (codeTemp.value.i < -0x8000 || codeTemp.value.i > 0x7FFF) return false; |
break; |
case 4: // 4 bytes |
if (-codeTemp.value.i > 0x80000000u || codeTemp.value.i > 0x7FFFFFFF) return false; |
break; |
} |
} |
else { |
// immediate is floating point or small integer converted to floating point |
int fit = code.fitNum; |
if ((code.dtype & 0xFF) <= (TYP_FLOAT32 & 0xFF)) fit |= FFIT_32; |
switch (code.formatp->immSize) { |
case 0: // no immediate |
return false; |
case 1: // 1 byte |
if (!(fit & IFIT_I8)) return false; |
break; |
case 2: // 2 bytes |
if (!(fit & FFIT_16)) return false; |
break; |
case 4: // 4 bytes |
if (!(fit & FFIT_32)) return false; |
break; |
case 8: // 8 bytes., currently not supported |
; |
} |
} |
} |
else { |
// immediate integer operand |
switch (code.formatp->immSize) { |
case 0: // no immediate |
return false; |
case 1: |
if (codeTemp.fitNum & IFIT_I8) break; // fits |
if ((codeTemp.dtype & 0x1F) == (TYP_INT8 & 0x1F) && (codeTemp.fitNum & IFIT_U8)) break; // 8 bit size fits unsigned with no sign extension |
return false; |
case 2: // 2 bytes |
if (instructionlistId[ii].opimmediate == OPI_INT1632) { // 16+32 bits |
if ((codeTemp.value.u >> 32) <= 0xFFFF) break; |
return false; |
} |
if (codeTemp.fitNum & IFIT_I16) break; // fits |
if ((codeTemp.dtype & 0x1F) == (TYP_INT16 & 0x1F) && (codeTemp.fitNum & IFIT_U16)) break; // 16 bit size fits unsigned with no sign extension |
return false; |
case 4: // 4 bytes |
if (instructionlistId[ii].opimmediate == OPI_2INT16) { // 16+16 bits |
if (codeTemp.value.w <= 0xFFFF && (codeTemp.value.u >> 32) <= 0xFFFF) break; |
return false; |
} |
if (codeTemp.fitNum & IFIT_I32) break; // fits |
if ((codeTemp.dtype & 0x1F) == (TYP_INT32 & 0x1F) && (codeTemp.fitNum & IFIT_U32)) break; // 32 bit size fits unsigned with no sign extension |
return false; |
case 8: // 8 bytes |
break; |
default: // does not fit other sizes |
return false; |
} |
} |
} |
else { |
// no explicit immediate |
if (code.formatp->immSize && code.instruction != II_JUMP && code.instruction != II_CALL) return false; |
} |
|
// memory operand |
if (code.etype & XPR_MEM) { |
if (code.formatp->mem == 0) return false; // memory operand requested but not supported |
uint32_t scale2 = formatOT; |
if (scale2 > 4) scale2 -= 3; // operand size = 1 << scale2 |
if (code.etype & XPR_SYM1) { // has data symbol |
if (code.etype & XPR_SYM2) { // has difference between two symbols |
codeTemp.sizeUnknown = 1; |
} |
if (!(code.fitAddr & IFIT_I32)) return false; // assume symbol address requires 32 bits. local symbol difference resolved later when sizeUnknown = 1 |
} |
// check index and scale factor |
if (code.etype & XPR_INDEX) { |
if (!(code.formatp->mem & 4)) return false; // index not supported |
} |
else { // no index requested |
if (code.formatp->mem & 4) { |
codeTemp.index = 0x1F; // RT = 0x1F means no index |
codeTemp.scale = 1 << scale2; |
} |
} |
|
// check address offset size |
if (code.etype & XPR_OFFSET) { |
if (!(code.formatp->mem & 0x10)) return false; // format does not support memory offset |
switch (code.formatp->addrSize) { |
case 1: // scale factor depends on operand size |
if (code.offset_mem & ((1 << scale2) - 1)) return false; // offset is not a multiple of the scale factor |
if ((code.offset_mem >> scale2) < -0x80 || (code.offset_mem >> scale2) > 0x7F) return false; |
break; |
case 2: |
if (!(code.fitAddr & IFIT_I16)) return false; |
break; |
case 4: |
if (!(code.fitAddr & IFIT_I32)) return false; |
break; |
default: |
return false; |
} |
} |
} |
else if (code.formatp->mem) return false; // memory operand supported by not requested |
|
return true; |
} |
|
|
// Check code for correctness before fitting a format, and fix some code details |
void CAssembler::checkCode1(SCode & code) { |
|
// check code for correctness |
if (code.etype & XPR_MEM) { |
// check memory operand |
bool useVectors = (code.dtype & TYP_FLOAT) != 0 || (code.dest & 0xE0) == REG_V || (code.reg1 & 0xE0) == REG_V; |
if (useVectors && code.scale == -1) { |
code.etype |= XPR_LENGTH; code.length = code.index; // index register is also length |
} |
int numOpt = ((code.etype & XPR_SCALAR) != 0) + ((code.etype & XPR_LENGTH) != 0) + ((code.etype & XPR_BROADC) != 0); |
if (numOpt > 1) {errors.reportLine(ERR_CONFLICT_OPTIONS); return;} // conflicting options |
if (numOpt && !useVectors && !(code.etype & XPR_SCALAR)) {errors.reportLine(ERR_VECTOR_OPTION); return;} // vector option on non-vector operands |
|
if (code.etype & XPR_INDEX) { |
// check scale factor |
const int dataSizeTable[8] = {1, 2, 4, 8, 16, 4, 8, 16}; // data size for each operant type |
int8_t scale = code.scale; |
if (scale != 1 && scale != -1 && scale != dataSizeTable[code.dtype & 7]) errors.reportLine(ERR_SCALE_FACTOR); |
if (code.scale == -1 && code.length && code.length != code.index) { |
errors.reportLine(ERR_NEG_INDEX_LENGTH); return; |
} |
} |
if (!(code.etype & XPR_BASE)) { |
// no base pointer. check if there is a symbol with an implicit base pointer |
int32_t symi1 = 0; |
if (code.etype & XPR_SYM1) symi1 = findSymbol(code.sym1); |
if ((code.etype & XPR_SYM2) || symi1 < 1 || !(symbols[symi1].st_other & STV_SECT_ATTR)) { |
errors.reportLine(ERR_NO_BASE); |
} |
} |
} |
// check mask |
if ((code.etype & XPR_MASK) && (code.mask & 0x1F) > 6) errors.reportLine(ERR_MASK_REGISTER); |
|
// check fallback |
if (code.etype & XPR_MASK) { |
if (code.fallback == 0) code.fallback = code.reg1 ? code.reg1 : 0x1F; // default fallback is reg1, or 0 if no reg1 |
if ((code.fallback & 0xE0) == 0) code.fallback |= code.dest & 0xE0; // get type of dest if fallback has no type |
} |
|
// details for unsigned variants |
if (code.dtype & TYP_UNS) { // an unsigned type is specified |
switch (code.instruction) { |
case II_DIV: case II_DIV_EX: |
case II_MUL_HI: case II_MUL_EX: |
case II_REM: case II_SHIFT_RIGHT_S: |
case II_MIN: case II_MAX: |
code.instruction |= 1; // change to unsigned version |
break; |
default:; // other instructions: do nothing |
} |
} |
|
// handle half precision |
if (uint8_t(code.dtype) == uint8_t(TYP_FLOAT16)) { |
switch (code.instruction) { |
case II_ADD: case II_MUL: case II_DIV: case II_MUL_ADD: |
code.instruction |= II_ADD_H & 0xFF000; // change to half precision instruction |
break; |
case II_SUB: |
if ((code.etype & XPR_IMMEDIATE) && !(code.etype & (XPR_MEM | XPR_REG2))) { |
code.instruction = II_ADD_H; code.value.d = - code.value.d; // subtract constant changed to add -constant |
} |
else code.instruction = II_SUB_H; |
break; |
case II_SUB_REV: |
if (code.value.i == 0) { // -x |
code.instruction = II_TOGGLE_BIT; |
code.value.u = 15; |
} |
else errors.reportLine(ERR_WRONG_OPERANDS); |
break; |
case II_MOVE: case II_REPLACE: case II_REPLACE_EVEN: case II_REPLACE_ODD: |
if (code.etype & XPR_INT) { // convert integer to float16 |
if (abs(code.value.i) > 65504) errors.reportLine(ERR_OVERFLOW); |
code.value.u = double2half(double(code.value.i)); |
} |
else if (code.etype & XPR_FLT) { // convert double to float16 |
if (code.value.d > 65504. || code.value.d < -65504.) errors.reportLine(ERR_OVERFLOW); |
code.value.u = double2half(code.value.d); |
code.etype = (code.etype & ~ XPR_IMMEDIATE) | XPR_INT; |
} |
if (code.instruction == II_SUB_H && (code.etype & XPR_IMMEDIATE)) { |
code.value.w ^= 0x8000; |
code.instruction &= ~1; // convert sub_h constant to add_h -constant |
} |
code.dtype = TYP_INT16; |
code.fitNum = IFIT_I16 | IFIT_I32; |
break; |
case II_STORE: |
if (code.etype & XPR_INT) code.value.u = double2half(double(code.value.i)); |
else code.value.u = double2half(code.value.d); |
code.dtype = TYP_INT16; |
code.etype = (code.etype & ~ XPR_FLT) | XPR_INT; |
break; |
case II_ADD_H: case II_SUB_H: case II_MUL_H: case II_DIV_H: case II_MUL_ADD_H: |
break; |
default: |
// no other instructions support half precision |
errors.reportLine(ERR_WRONG_OPERANDS); |
} |
} |
|
// special case instructions |
switch (code.instruction) { |
case II_STORE: |
if ((code.dtype & TYP_FLOAT) && (code.etype & XPR_FLT) && !(code.reg1)) { |
// store float constant |
// code.dtype = code.dtype + (TYP_INT32 - TYP_FLOAT32) | TYP_UNS; |
} |
} |
|
// check size needed for immediate operand and address |
fitConstant(code); |
fitAddress(code); |
|
if (code.instruction & II_JUMP_INSTR) { |
// jump instruction |
code.category = 4; |
// check register type |
if (code.dtype && code.reg1) { |
if ((code.dtype & 0xFF) <= (TYP_FLOAT16 & 0xFF)) { // must use g.p. registers |
if (code.reg1 & REG_V) errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
else { // must use vector registers |
if (code.reg1 & REG_R) errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
} |
// check if immediate operand too big |
if (code.etype & XPR_IMMEDIATE) { |
if (code.dtype & TYP_FLOAT) { |
if ((code.dtype & 0xFF) >= (TYP_FLOAT64 & 0xFF) && !(code.fitNum & FFIT_32)) errors.reportLine(ERR_TOO_LARGE_FOR_JUMP); |
} |
else if (code.dtype & TYP_UNS) { |
if ((code.dtype & 0x1F) >= (TYP_INT64 & 0x1F) && !(code.fitNum & IFIT_U32)) errors.reportLine(ERR_TOO_LARGE_FOR_JUMP); |
} |
else if ((code.dtype & 0x1F) >= (TYP_INT64 & 0x1F) && !(code.fitNum & IFIT_I32)) errors.reportLine(ERR_TOO_LARGE_FOR_JUMP); |
} |
} |
|
// optimize instruction |
if (cmd.optiLevel) optimizeCode(code); |
} |
|
|
// Check register types etc. after fitting a format, and finish code details |
void CAssembler::checkCode2(SCode & code) { |
if (code.instruction >= II_ALIGN) return; // not an instruction |
|
// check type |
if (code.dtype == 0) { |
if ((code.etype & (XPR_INT | XPR_FLT | XPR_REG | XPR_REG1 | XPR_MEM)) && !(variant & (VARIANT_D0 | VARIANT_D2))) { // type not specified |
if (code.instruction == II_MOVE && code.category == 3 && !(code.etype & (XPR_IMMEDIATE | XPR_MEM))) { |
// register-to-register move. find appropriate operand type |
code.dtype = TYP_INT64; // g.p. register. copy whole register ?? |
if (code.dest & REG_V) code.dtype = TYP_INT8; // vector register. length must be divisible by tpe |
} |
else { |
errors.reportLine(ERR_TYPE_MISSING); // type must be specified |
return; |
} |
} |
} |
|
if (code.etype & XPR_MEM) { |
// check memory operand |
if (variant & VARIANT_M0) { // memory destination |
if (code.etype & XPR_BROADC) { |
errors.reportLine(ERR_DEST_BROADCAST); return; |
} |
} |
if (code.base >= REG_R + 28 && code.base <= REG_R + 30 && (code.formatp->addrSize) > 1 && pass < 4) { |
// cannot use r28 - r30 as base pointer with more than 8 bits offset |
// (we don't get an error message here for a symbol address because the base pointer has not been assigned yet) |
errors.reportLine(ERR_R28_30_BASE); |
} |
// check M1 option |
/*if (variant & VARIANT_M1) { |
if (code.formatp->tmplate == 0xE && (code.etype & XPR_MEM) && (code.etype & XPR_INT) |
&& (code.value.i > 63 || code.value.i < -63)) { |
errors.reportLine(ERR_CONSTANT_TOO_LARGE); return; |
} |
if (code.optionbits && (code.etype & XPR_MEM)) { |
errors.reportLine(ERR_BOTH_MEM_AND_OPTIONS); return; |
} |
}*/ |
} |
|
if (lineError) return; // skip additional errors |
|
// Make list of operands from available operands. 0=none, 1=immediate, 2=memory, 5=RT, 6=RS, 7=RU, 8=RD |
uint8_t opAvail = code.formatp->opAvail; // Bit index of available operands |
int j; // loop counter |
|
// check if correct number of registers |
uint32_t numReq = instructionlistId[code.instr1].sourceoperands; // number of registers required for this instruction |
if (code.category == 4 && (code.instruction & II_JUMP_INSTR) && (code.etype & XPR_JUMPOS) && numReq) numReq--; |
if ((code.etype & XPR_IMMEDIATE) && numReq) numReq--; |
if ((code.etype & XPR_INT2) && numReq) numReq--; |
if ((code.etype & XPR_MEM) && !(variant & VARIANT_M0) && numReq) numReq--; |
|
uint32_t nReg = 0; |
for (j = 0; j < 3; j++) nReg += (code.etype & (XPR_REG1 << j)) != 0; |
if (nReg < numReq && !(variant & VARIANT_D3)) |
errors.reportLine(ERR_TOO_FEW_OPERANDS); |
else if (nReg > numReq && instructionlistId[code.instr1].opimmediate != 25) { |
errors.reportLine(ERR_TOO_MANY_OPERANDS); |
} |
|
// count number of available registers in format |
uint32_t regAvail = 0; |
opAvail >>= 4; // register operands |
while (opAvail) { |
regAvail += opAvail & 1; |
opAvail >>= 1; |
} |
|
// expected register types |
uint8_t regType = REG_R; |
if ((code.formatp->vect & 1) || ((code.formatp->vect & 0x10) && (code.dtype & 4))) regType = REG_V; |
|
// check each of up to three source registers |
for (j = 0; j < 3; j++) { |
if (code.etype & (XPR_REG1 << j)) { // register j used |
if (variant & VARIANT_SPECS) { // must be special register |
if (((&code.reg1)[j] & 0xE0) <= REG_V) errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
else if ((variant & (VARIANT_R1 << j)) |
|| ((variant & VARIANT_RL) && (j == 2 || (&code.reg1)[j+1] == 0))) { |
if (((&code.reg1)[j] & 0xE0) != REG_R) { // this operand must be general purpose register |
errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
} |
else if (((&code.reg1)[j] & 0xE0) != regType) { // wrong register type |
errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
} |
if (lineError) return; // skip additional errors |
} |
// check destination register |
if (code.dest) { |
if (variant & VARIANT_SPECD) { // must be special register |
if ((code.dest & 0xE0) <= REG_V) errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
else if (variant & VARIANT_R0) { |
if ((code.dest & 0xE0) != REG_R) { // destination must be general purpose register |
errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
} |
else if ((code.dest & 0xE0) != regType && code.dest != 2) { // wrong register type |
errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
else if ((code.dest == 2) ^ ((variant & VARIANT_M0) != 0)) { // operands in wrong order |
errors.reportLine(ERR_OPERANDS_WRONG_ORDER); |
} |
|
if (lineError) return; // skip additional errors |
} |
if ((variant & (VARIANT_D0 | VARIANT_D1 | VARIANT_D2)) != 0 && code.dest != 0) { // should not have destination |
errors.reportLine(ERR_NO_DESTINATION); |
} |
if ((variant & (VARIANT_D0 | VARIANT_D1)) == 0 && code.dest == 0) { // should have destination |
errors.reportLine(ERR_MISSING_DESTINATION); |
} |
|
// check mask register |
if ((code.etype & XPR_FALLBACK) && !(code.etype & XPR_MASK)) { // fallback but no mask |
code.mask = 7; // no mask |
} |
if ((code.etype & (XPR_MASK | XPR_FALLBACK)) && (code.mask & 7) != 7) { // mask used |
if ((code.mask & 0xE0) != regType) { // wrong type for mask register |
errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
else if ((code.fallback & 0xE0) != regType && (code.fallback & 0x1F) != 0x1F) { // wrong type for fallback registser |
if ((variant & VARIANT_RL) && code.fallback == code.reg1) { |
// fallback has been assigned to reg1 in CAssembler::checkCode1, but reg1 is g.p. register |
code.fallback = 0x5F; |
} |
else errors.reportLine(ERR_WRONG_REG_TYPE); |
} |
if ((code.etype & XPR_FALLBACK) && (variant & VARIANT_F0)) { // cannot have fallback register |
errors.reportLine(ERR_CANNOT_HAVEFALLBACK1); |
} |
// check if fallback is the right register |
if (code.etype & XPR_FALLBACK) { |
if (code.numOp >= 3 && code.fallback != code.reg1) { |
errors.reportLine(ERR_3OP_AND_FALLBACK); |
} |
} |
} |
|
// check scale factor |
const int dataSizeTable[8] = { 1, 2, 4, 8, 16, 4, 8, 16 }; // data size for each operant type |
int8_t scale = code.scale; |
if (scale == 0) scale = 1; |
if (((code.formatp->scale & 4) && scale != -1) // scale must be -1 |
|| (((code.formatp->scale & 6) == 2) && scale != dataSizeTable[code.dtype & 7]) // scale must match operand type |
|| (((code.formatp->scale & 6) == 0 && scale != 1 && (code.index & 0x1F) != 0x1F))) { // scale must be 1 |
errors.reportLine(ERR_SCALE_FACTOR); |
} |
// check vector length |
int numOpt = ((code.etype & XPR_SCALAR) != 0) + ((code.etype & XPR_LENGTH) != 0) + ((code.etype & XPR_BROADC) != 0); |
if (numOpt == 0 && (code.etype & XPR_MEM) && (code.formatp->vect & ~0x10) && !(code.etype & XPR_LIMIT) && !(code.formatp->vect & 0x80)) { |
errors.reportLine(ERR_LENGTH_OPTION_MISS); return; // missing length option |
} |
|
// check immediate type |
if ((code.etype & XPR_FLT) && (variant & VARIANT_I2)) { |
// immediate should be integer |
code.etype = (code.etype & ~XPR_FLT) | XPR_INT; |
//code.value.i = (int64_t)code.value.d; |
code.value.i = value0; |
} |
if ((code.etype & XPR_INT) && !(code.etype & (XPR_LIMIT | XPR_INT2))) { |
// check if value fits specified operand type |
int ok = 1; |
switch (code.dtype & 0x1F) { |
case TYP_INT8 & 0x1F: |
ok = code.fitNum & (IFIT_I8 | IFIT_U8); break; |
case TYP_INT16 & 0x1F: |
ok = code.fitNum & (IFIT_I16 | IFIT_U16); break; |
case TYP_INT32 & 0x1F: |
ok = code.fitNum & (IFIT_I32 | IFIT_U32); break; |
} |
if (!ok && (instructionlistId[code.instr1].opimmediate & ~0x10) != OPI_INT32) { |
errors.reportLine(ERR_CONSTANT_TOO_LARGE); |
} |
} |
|
// check options |
if ((code.etype & XPR_OPTIONS) && !(variant & VARIANT_On) && code.formatp->category != 4) { |
errors.reportLine(ERR_CANNOT_HAVE_OPTION); |
} |
|
// details for unsigned variants |
if (code.dtype & TYP_UNS) { // an unsigned type is specified |
if ((variant & VARIANT_U3) && code.instruction == II_COMPARE && code.optionbits) code.optionbits |= 8; // unsigned compare |
} |
|
if (section) code.section = section; // insert section |
} |
|
|
// find reason why no format fits, and return error number |
uint32_t CAssembler::checkCodeE(SCode & code) { |
// check fallback |
if ((code.etype & XPR_FALLBACK) && code.fallback != code.dest) { |
if (((code.etype & XPR_MEM) && (code.dest & REG_V)) || code.index) return ERR_CANNOT_HAVEFALLBACK2; |
if (instructionlistId[code.instr1].sourceoperands >= 3) return ERR_3OP_AND_FALLBACK; |
} |
// check three-operand instructions |
if (instructionlistId[code.instr1].sourceoperands >= 3 && code.reg1 != code.dest && (code.etype & XPR_MEM) && ((code.dest & REG_V) || code.index)) { |
return ERR_3OP_AND_MEM; |
} |
return ERR_NO_INSTRUCTION_FIT; // any other reason |
} |
|
|
// optimize instruction. replace by more efficient instruction if possible |
void CAssembler::optimizeCode(SCode & code) { |
|
// is it a vector instruction? |
bool hasVector = ((code.dest | code.reg1) & REG_V) != 0; |
|
// is it a floating point instruction? |
bool isFloat = (code.dtype & TYP_FLOAT) != 0; |
|
if (code.instruction & II_JUMP_INSTR) { |
// jump instruction |
// optimize immediate jump offset operand |
if ((code.instruction & 0xFF) == II_SUB && (code.etype & XPR_IMMEDIATE) == XPR_INT |
&& code.value.i >= -0x7F && code.value.i <= 0x80 && cmd.optiLevel |
&& ((code.dtype & 0xFF) == (TYP_INT32 & 0xFF) || ((code.dtype & 0xFF) <= (TYP_INT32 & 0xFF) && (code.dtype & TYP_PLUS)))) { |
// subtract with conditional jump with 8-bit immediate and 8-bit address |
// should be replaced by addition of the negative constant |
int32_t isym = 0; |
if (code.etype & XPR_SYM1) isym = findSymbol(code.sym1); |
if (isym <= 0 || symbols[isym].st_section == section || code_size <= (1 << 9)) { |
// we are not sure yet, but chances are good that the address fits an 8-bit field. Replace sub by add |
code.value.i = -code.value.i; // change sign of immediate constant |
code.instruction ^= (II_SUB ^ II_ADD); // replace sub with add |
if ((code.instruction & 0xFFFF00) == II_JUMP_CARRY) code.instruction ^= 0x100; // carry condition is inverted |
} |
} |
if ((code.fitNum & (IFIT_J16 | IFIT_J32) && (code.etype & XPR_IMMEDIATE) == XPR_INT && (code.instruction & 0xFE) == II_ADD)) { |
// replace add with sub or vice versa |
code.value.i = -code.value.i; // change sign of immediate constant |
code.instruction ^= (II_SUB ^ II_ADD); |
if ((code.instruction & 0xFFFF00) == II_JUMP_CARRY) code.instruction ^= 0x100; // carry condition is inverted |
code.fitNum |= (code.fitNum & IFIT_J) >> 1; // signal that it fits |
} |
} |
else { // other instruction. optimize immediate operand |
if ((code.etype & XPR_INT) /* && !(code.etype & (XPR_OFFSET | XPR_LIMIT | XPR_SYM1))*/ ) { |
if ((code.instruction & 0xFFFFFFFE) == II_ADD && (code.fitNum & IFIT_J8) != 0) { |
// we can make the instruction smaller by changing the sign of the constant and exchange add and sub |
// (we don't have to do this for 0x8000 and 0x80000000 because the can be fitted as 1 << x) |
code.instruction ^= (II_ADD ^ II_SUB); // replace add with sub or vice versa |
code.value.i = -code.value.i; // change sign of immediate constant |
code.fitNum |= (code.fitNum & IFIT_J) >> 1; // signal that it fits |
} |
else if (code.instruction == II_SUB && (code.fitNum & (IFIT_I16SH16 | IFIT_I16)) && !(code.fitNum & IFIT_I8) |
&& code.value.w != 0x80000000U && code.value.w != 0xFFFF8000U && code.dest == code.reg1 && !hasVector |
&& (((uint8_t)code.dtype == (uint8_t)TYP_INT32) || (((uint8_t)code.dtype < (uint8_t)TYP_INT32) && (code.dtype & TYP_PLUS)))) { |
code.instruction = II_ADD; // replace sub with add |
code.value.i = -code.value.i; // change sign of immediate constant |
} |
else if (code.instruction == II_SUB && (code.fitNum & IFIT_I8SHIFT) && !(code.fitNum & IFIT_I8) && !isFloat |
&& code.dest == code.reg1 |
&& (((uint8_t)code.dtype >= (uint8_t)TYP_INT32) || (code.dtype & TYP_PLUS))) { |
code.instruction = II_ADD; // replace sub with add |
code.value.i = -code.value.i; // change sign of immediate constant |
code.fitNum &= ~(IFIT_I16 | IFIT_I16SH16 | IFIT_I32SH32); |
} |
else if (code.instruction == II_SUB && (code.fitNum & IFIT_I32SH32) && !(code.fitNum & (IFIT_I16SHIFT | IFIT_I32)) |
&& (((uint8_t)code.dtype == (uint8_t)TYP_INT64) || (code.dtype & TYP_PLUS)) && !isFloat) { |
code.instruction = II_ADD; // replace sub with add |
code.value.i = -code.value.i; // change sign of immediate constant |
} |
else if ((code.instruction == II_MOVE || code.instruction == II_AND) |
&& (code.fitNum & IFIT_U32) && !(code.fitNum & (IFIT_I32 | IFIT_I16SHIFT)) |
&& ((uint8_t)code.dtype == (uint8_t)TYP_INT64) && !hasVector) { |
code.dtype = TYP_INT32; // changing type to int32 will zero extend |
} |
/*else if (code.instruction == II_MOVE |
&& (code.fitNum & IFIT_U16) && !(code.fitNum & IFIT_I16) |
&& ((uint8_t)code.dtype >= (uint8_t)TYP_INT32) && !hasVector |
&& !(code.etype & (XPR_REG | XPR_MEM | XPR_OPTION | XPR_SYM1))) { |
code.instruction = II_MOVE_U; |
code.dtype = TYP_INT64; |
} */ |
else if (code.instruction == II_OR && (code.value.u & (code.value.u-1)) == 0 && !(code.fitNum & IFIT_I8)) { |
code.instruction = II_SET_BIT; // OR with a power of 2 |
code.value.u = bitScanReverse(code.value.u); |
code.fitNum = IFIT_I8 | IFIT_I16 | IFIT_I32; |
} |
else if (code.instruction == II_AND && (~code.value.u & (~code.value.u-1)) == 0 && !(code.fitNum & IFIT_I8)) { |
code.instruction = II_CLEAR_BIT; // AND with ~(a power of 2) |
code.value.u = bitScanReverse(~code.value.u); |
code.fitNum = IFIT_I8 | IFIT_I16 | IFIT_I32; |
} |
else if (code.instruction == II_XOR && (code.value.u & (code.value.u-1)) == 0 && !(code.fitNum & IFIT_I8)) { |
code.instruction = II_TOGGLE_BIT; // XOR with a power of 2 |
code.value.u = bitScanReverse(code.value.u); |
code.fitNum = IFIT_I8 | IFIT_I16 | IFIT_I32; |
} |
} |
if ((code.etype & XPR_FLT) && !(code.etype & (XPR_OFFSET | XPR_LIMIT | XPR_SYM1))) { |
if (code.instruction == II_SUB && (code.fitNum & FFIT_16) && (uint8_t)code.dtype >= (uint8_t)TYP_FLOAT16) { |
code.instruction = II_ADD; // replace sub with add |
code.value.d = -code.value.d; // change sign of immediate constant |
} |
} |
} |
|
// optimize -float as toggle_bit |
if (code.instruction == II_SUB_REV && (code.etype & XPR_IMMEDIATE) && (code.dtype & TYP_FLOAT) |
&& code.value.i == 0 && (code.etype & XPR_REG1) && !(code.etype & XPR_REG2)) { |
// code is -v represented as 0-v. replace by flipping bit |
uint32_t bits = 1 << (code.dtype & 7); // number of bits in floating point type |
code.instruction = II_TOGGLE_BIT; |
code.value.u = bits - 1; |
code.etype = ((code.etype & ~XPR_IMMEDIATE) | XPR_INT); |
} |
|
// optimize multiply and divide instructions |
if ((code.instruction == II_MUL || code.instruction == II_DIV) && (code.etype & XPR_IMMEDIATE)) { |
if (code.dtype & TYP_INT) { // integer multiplication |
// check if constant is positive and a power of 2 |
if (code.value.i <= 0 || (code.value.u & (code.value.u - 1))) return; |
if (code.instruction == II_MUL) { |
// integer multiplication by power of 2. replace by left shift |
code.instruction = II_SHIFT_LEFT; |
code.value.u = bitScanReverse(code.value.u); |
} |
else if (code.dtype & TYP_UNS) { |
// unsigned division by power of 2. replace by right shift |
// We are not optimizing signed division because this requires multiple instructions and registers |
code.instruction = II_SHIFT_RIGHT_U; |
code.value.u = bitScanReverse(code.value.u); |
} |
} |
else if (code.dtype & TYP_FLOAT) { |
// floating point multiplication or division |
// check if constant is a power of 2 |
int shiftCount = 0xFFFFFFFF; // shift count to replace multiplication by power of 2 |
if ((code.etype & XPR_INT) && code.value.i > 0 && (code.value.u & (code.value.u-1)) == 0) { |
// positive integer power of 2 |
shiftCount = bitScanReverse(code.value.u); |
if (code.instruction == II_DIV) shiftCount = -shiftCount; |
} |
else if ((code.etype & XPR_FLT) && code.value.d != 0.) { |
int32_t exponent = (code.value.u >> 52) & 0x7FF; // exponent field of double |
if ((code.value.u & ((uint64_t(1) << 52) - 1)) == 0 && exponent != 0 && exponent != 0x7FF) { |
// value is a power of 2, and not inf, nan, or subnormal |
shiftCount = exponent - 0x3FF; |
if (code.instruction == II_DIV) shiftCount = -shiftCount; |
} |
} |
if (shiftCount == (int)0xFFFFFFFF) return; // not a power of 2. cannot optimize |
if (shiftCount >= 0 || cmd.optiLevel >= 3) { |
// replace by mul_2pow instruction |
// use negative powers of 2 only in optimization level 3, because subnormals are ignored |
code.instruction = II_MUL_2POW; |
code.value.i = shiftCount; |
code.etype = (code.etype & ~XPR_IMMEDIATE) | XPR_INT; |
} |
else if (code.instruction == II_DIV) { |
// replace division by power of 2 to multiplication by the reciprocal |
code.instruction = II_MUL; |
if (code.etype & XPR_FLT) code.value.d = 1. / code.value.d; |
else { |
code.value.d = 1. / double((uint64_t)1 << (-shiftCount)); |
code.etype = (code.etype & ~XPR_IMMEDIATE) | XPR_FLT; |
} |
} |
} |
} |
} |
|
|
void insertMem(SCode & code, SExpression & expr) { |
// insert memory operand into code structure |
if (code.value.i && expr.value.i) code.etype |= XPR_ERROR; // both have constants |
if (expr.etype & XPR_OFFSET) code.offset_mem = expr.offset_mem; |
else code.offset_mem = expr.value.w; |
code.etype |= expr.etype; |
code.tokens += expr.tokens; |
code.sym1 = expr.sym1; |
code.sym2 = expr.sym2; |
code.base = expr.base; |
code.index = expr.index; |
code.length = expr.length; |
code.scale = expr.scale; |
code.symscale1 = expr.symscale1; |
code.mask |= expr.mask; |
code.fallback |= expr.fallback; |
} |
|
void insertAll(SCode & code, SExpression & expr) { |
// insert everything from expression to code structure, OR'ing all bits |
for (uint32_t i = 0; i < sizeof(SExpression) / sizeof(uint64_t); i++) { |
(&code.value.u)[i] |= (&expr.value.u)[i]; |
} |
} |