OpenCores
URL https://opencores.org/ocsvn/forwardcom/forwardcom/trunk

Subversion Repositories forwardcom

[/] [forwardcom/] [bintools/] [assem4.cpp] - Rev 166

Go to most recent revision | Compare with Previous | Blame | View Log

/****************************    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];
    } 
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.