URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 44 to Rev 45
- ↔ Reverse comparison
Rev 44 → Rev 45
/forwardcom/bintools/assem5.cpp
0,0 → 1,1814
/**************************** assem5.cpp ******************************** |
* Author: Agner Fog |
* Date created: 2017-09-19 |
* Last modified: 2021-05-21 |
* Version: 1.11 |
* Project: Binary tools for ForwardCom instruction set |
* Module: assem.cpp |
* Description: |
* Module for assembling ForwardCom .as files. |
* This module contains functions for interpreting high level language constructs: |
* functions, branches, and loops |
* |
* Copyright 2017-2021 GNU General Public License http://www.gnu.org/licenses |
******************************************************************************/ |
#include "stdafx.h" |
|
// Define high level block types |
const int HL_SECTION = 1; // section |
const int HL_FUNC = 2; // function |
const int HL_IF = 3; // if branch |
const int HL_ELSE = 4; // else branch |
const int HL_SWITCH = 5; // switch-case branch |
const int HL_FOR = 6; // for loop |
const int HL_FOR_IN = 7; // vector loop. for (v1 in [r2-r3]) {} |
const int HL_WHILE = 8; // while loop |
const int HL_DO_WHILE = 9; // do-while loop |
|
// invert condition code for branch instruction |
void invertCondition(SCode & code) { |
code.instruction ^= II_JUMP_INVERT; // invert condition code |
if ((code.dtype & TYP_FLOAT) |
&& (code.instruction & 0xFF) == II_COMPARE |
&& (code.instruction & 0x7F00) - 0x1000 < 0x2000) { |
// floating point compare instructions, except jump_ordered, must invert the unordered bit |
code.instruction ^= II_JUMP_UNORDERED; // inverse condition is unordered |
} |
} |
|
// if, else, switch, for, do, while statements |
void CAssembler::interpretHighLevelStatement() { |
//uint32_t label = 0; |
if (tokenN > 2 && tokens[tokenB].type == TOK_SYM && tokens[tokenB+1].id == ':') { |
// line starts with a label. insert label |
// (this will prevent merging of jump instruction. merging is not allowed when there is a label between the two instructions) |
SCode codeL; |
zeroAllMembers(codeL); |
//codeL.label = tokens[tokenB].value.w; //? |
codeL.label = tokens[tokenB].id; |
codeL.section = section; |
codeBuffer.push(codeL); |
// interpret directive after label |
tokenB += 2; |
tokenN -= 2; |
} |
uint32_t tok = tokenB; |
if (tokenN > 1 && tokens[tok].type == TOK_TYP) { |
tok++; // skip type keyword |
if (tok+1 < tokenB+tokenN && tokens[tok].type == TOK_OPR && tokens[tok].id == '+') tok++; // skip '+' after type |
} |
// expect HLL keyword here. dispatch to the corresponding function |
switch (tokens[tok].id) { |
case HLL_IF: |
codeIf(); break; |
case HLL_SWITCH: |
codeSwitch(); break; |
case HLL_CASE: |
codeCase(); break; |
case HLL_FOR: |
codeFor(); break; |
case HLL_WHILE: |
codeWhile(); break; |
case HLL_DO: |
codeDo(); break; |
case HLL_BREAK: case HLL_CONTINUE: |
if (tok != tokenB) { |
errors.report(tokens[tok]); // cannot have type token before break or continue |
break; |
} |
codeBreak(); break; |
case HLL_PUSH: // may be replaced by macro later |
codePush(); break; |
case HLL_POP: // may be replaced by macro later |
codePop(); break; |
default: |
errors.report(tokens[tok]); |
} |
} |
|
|
// finish {} block |
void CAssembler::interpretEndBracket() { |
uint32_t n = hllBlocks.numEntries(); |
if (n == 0) { |
errors.reportLine(ERR_BRACKET_END); // unmatched end bracket |
return; |
} |
// dispatch depending on type of block |
switch (hllBlocks[n-1].blockType) { |
case HL_FUNC: // function |
break; |
case HL_IF: case HL_ELSE: // if branch |
codeIf2(); |
break; |
case HL_FOR: // for loop |
codeFor2(); |
break; |
case HL_FOR_IN: // vector loop. for (v1 in [r2-r3]) {} |
codeForIn2(); |
break; |
case HL_WHILE: // while loop |
codeWhile2(); |
break; |
case HL_DO_WHILE: // do-while loop |
codeDo2(); |
break; |
case HL_SWITCH: // switch-case branch |
break; |
default: |
errors.reportLine(ERR_BRACKET_END); // should not occur |
} |
} |
|
|
// Interpret if statement in assembly code |
void CAssembler::codeIf() { |
uint32_t state = 0; // 0: start, 1: after type, 2: after if, 3: after (, 4: after (type, |
// 5: after expression, 6: after ')', 7: after { |
uint32_t tok; // current token index |
SBlock block; // block descriptor to save |
zeroAllMembers(block); // reset |
block.blockType = HL_IF; // if block |
SToken token; // current token |
SExpression expr; // expression in () |
SCode code; // instruction code |
zeroAllMembers(code); // reset |
|
// interpret line by state machine looping through tokens |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
if (lineError) break; |
token = tokens[tok]; |
|
switch (state) { |
case 0: // start. expect type or 'if' |
if (token.type == TOK_TYP) { |
code.dtype = dataType = token.id & 0xFF; |
state = 1; |
} |
else if (token.id == HLL_IF) state = 2; |
else errors.report(token); |
break; |
case 1: // after type. expect '+' or 'if' |
if (token.type == TOK_OPR && token.id == '+') { |
code.dtype |= TYP_PLUS; |
} |
else if (token.id == HLL_IF) state = 2; |
else errors.report(token); |
break; |
case 2: // after if. expect '(' |
if (token.type == TOK_OPR && token.id == '(') state = 3; |
else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS); |
break; |
case 3: // after '('. expect type or logical expression |
if (token.type == TOK_TYP && !code.dtype) { |
code.dtype = dataType = token.id & 0xFF; |
state = 4; |
break; |
} |
EXPRESSION: |
expr = expression(tok, tokenB + tokenN-tok, (code.dtype & TYP_UNS) != 0); |
if (lineError) return; |
|
// insert logical expression into block |
insertAll(code, expr); |
tok += expr.tokens - 1; |
state = 5; |
break; |
case 4: // after "if (type". expect '+' or expression |
if (token.type == TOK_OPR && token.id == '+') { |
code.dtype |= TYP_PLUS; |
break; |
} |
// not a '+'. expect expression |
goto EXPRESSION; |
case 5: // after expression. expect ')' |
if (token.type == TOK_OPR && token.id == ')') state = 6; |
else { |
errors.report(token); return; |
} |
break; |
} |
} |
// should end at state 6 because '{' should be on next pseudo-line |
if (state != 6) errors.report(token); |
if (lineError) return; |
|
if (linei == lines.numEntries()-1) { // no more lines |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
|
// get next line |
if (linei == lines.numEntries()-1) { // no more lines |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
lineError = false; |
|
// expect '{' |
if (tokens[tokenB].id != '{') { |
errors.reportLine(ERR_EXPECT_BRACKET); |
return; |
} |
// interpret the condition expression |
linei--; // make any error message apply to previous line |
interpretCondition(code); |
linei++; |
// make instruction code |
code.etype |= XPR_JUMPOS | XPR_SYM1; |
code.section = section; |
|
// check if {} contains a jump only |
uint32_t target2 = hasJump(linei+1); |
if (target2) { |
if (linei + 2 < lines.numEntries() && lines[linei+2].numTokens == 1) { |
tok = lines[linei+2].firstToken; |
if (tokens[tok].type == TOK_OPR && tokens[tok].id == '}') { |
// the {} block contains a jump and nothing else |
// make conditional jump to target2 instead |
code.sym5 = target2; |
linei += 2; // finished processing these two lines |
// check if it can be merged with previous instruction |
mergeJump(code); |
// finish code and fit it |
checkCode1(code); |
if (lineError) return; |
fitCode(code); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(code);// save code structure |
|
// check if there is an 'else' after if(){} |
if (linei + 2 < lines.numEntries() && lines[linei+1].numTokens == 1 && lines[linei+2].numTokens == 1) { |
tok = lines[linei+1].firstToken; |
if (tokens[tok].type == TOK_HLL && tokens[tok].id == HLL_ELSE) { |
tok = lines[linei+2].firstToken; |
if (tokens[tok].type == TOK_OPR && tokens[tok].id == '{') { |
// make the 'else' ignored |
linei += 2; |
// make block record with no label |
block.blockNumber = ++iIf; |
block.startBracket = tok; |
block.jumpLabel = 0; |
// store block in hllBlocks stack. will be retrieved at matching '}' |
hllBlocks.push(block); |
} |
} |
} |
return; |
} |
} |
} |
invertCondition(code); // invert condition. jump to else block if logical expression false |
|
if (code.instruction == (II_JUMP | II_JUMP_INVERT)) { |
// constant: don't jump |
code.instruction = 0; |
} |
// make block record with label name |
block.blockNumber = ++iIf; |
block.startBracket = tokenB; |
char name[32]; |
sprintf(name, "@if_%u_a", iIf); |
uint32_t symi = makeLabelSymbol(name); |
block.jumpLabel = symbols[symi].st_name; |
|
// store block in hllBlocks stack. will be retrieved at matching '}' |
hllBlocks.push(block); |
|
// store jump instruction |
code.sym5 = block.jumpLabel; |
|
// check if it can be merged with previous instruction |
mergeJump(code); |
|
// finish code and fit it |
checkCode1(code); |
if (lineError) return; |
fitCode(code); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(code);// save code structure |
} |
|
|
// Finish if statement at end bracket |
void CAssembler::codeIf2() { |
SCode code; // code record for jump label |
zeroAllMembers(code); // reset |
SBlock block = hllBlocks.pop(); // pop the stack of {} blocks |
uint32_t labelA = block.jumpLabel; // jump target label to place here |
// check if there is an 'else' following the if(){} |
if (block.blockType == HL_IF && linei+2 < lines.numEntries() && tokens[lines[linei+1].firstToken].id == HLL_ELSE) { |
// there is an else. get next line with the else |
linei++; |
if (lines[linei].numTokens > 1) errors.report(tokens[lines[linei].firstToken+1]); // something other than '{' after else |
// check if there is a '{' following the 'else' |
linei++; |
uint32_t tokenB = lines[linei].firstToken; |
if (lines[linei].numTokens > 1 || tokens[tokenB].type != TOK_OPR || tokens[tokenB].id != '{') { |
errors.reportLine(ERR_EXPECT_BRACKET); // expecting '{' |
return; |
} |
// make block record for jump to label b |
block.blockType = HL_ELSE; // if-else block |
block.startBracket = tokenB; |
// make label name |
char name[32]; |
sprintf(name, "@if_%u_b", block.blockNumber); |
uint32_t symi = makeLabelSymbol(name); |
block.jumpLabel = symbols[symi].st_name; |
hllBlocks.push(block); // store block in hllBlocks stack. will be retrieved at matching '}' |
|
// make jump instruction |
code.section = section; |
code.instruction = II_JUMP; |
code.etype = XPR_JUMPOS | XPR_SYM1; |
code.sym5 = block.jumpLabel; |
|
// check if it can be merged with previous instruction |
mergeJump(code); |
|
// finish code and save it |
checkCode1(code); |
if (lineError) return; |
fitCode(code); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(code);// save code structure |
} |
// make target label here |
if (labelA) { |
zeroAllMembers(code); |
code.section = section; |
code.label = labelA; // jump target label |
codeBuffer.push(code); // save code structure |
} |
} |
|
|
// Interpret while loop in assembly code |
void CAssembler::codeWhile() { |
uint32_t state = 0; // 0: start, 1: after type, 2: after while, 3: after (, 4: after (type, |
// 5: after expression, 6: after ')', 7: after { |
uint32_t tok; // current token index |
SBlock block; // block descriptor to save |
zeroAllMembers(block); // reset |
SToken token; // current token |
SExpression expr; // expression in () |
SCode code; // instruction code |
zeroAllMembers(code); // reset |
|
// interpret line by state machine looping through tokens (same as for codeIf) |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
if (lineError) break; |
token = tokens[tok]; |
|
switch (state) { |
case 0: // start. expect type or 'while' |
if (token.type == TOK_TYP) { |
code.dtype = dataType = token.id & 0xFF; |
state = 1; |
} |
else if (token.id == HLL_WHILE) state = 2; |
else errors.report(token); |
break; |
case 1: // after type. expect '+' or 'while' |
if (token.type == TOK_OPR && token.id == '+') { |
code.dtype |= TYP_PLUS; |
} |
else if (token.id == HLL_WHILE) state = 2; |
else errors.report(token); |
break; |
case 2: // after if. expect '(' |
if (token.type == TOK_OPR && token.id == '(') state = 3; |
else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS); |
break; |
case 3: // after '('. expect type or logical expression |
if (token.type == TOK_TYP && !code.dtype) { |
code.dtype = dataType = token.id & 0xFF; |
state = 4; |
break; |
} |
EXPRESSION: |
expr = expression(tok, tokenB + tokenN-tok, (code.dtype & TYP_UNS) != 0); |
if (lineError) return; |
|
// insert logical expression into block |
insertAll(code, expr); |
tok += expr.tokens - 1; |
state = 5; |
break; |
case 4: // after "while (type". expect '+' or expression |
if (token.type == TOK_OPR && token.id == '+') { |
code.dtype |= TYP_PLUS; |
break; |
} |
// not a '+'. expect expression |
goto EXPRESSION; |
case 5: // after expression. expect ')' |
if (token.type == TOK_OPR && token.id == ')') state = 6; |
else { |
errors.report(token); return; |
} |
break; |
} |
} |
// should end at state 6 because '{' should be on next pseudo-line |
if (state != 6) errors.report(token); |
if (lineError) return; |
|
if (linei == lines.numEntries()-1) { // no more lines |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
|
// get next line |
// get next line |
if (linei == lines.numEntries()-1) { // no more lines |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
lineError = false; |
|
// expect '{' |
if (tokens[tokenB].id != '{') { |
errors.reportLine(ERR_EXPECT_BRACKET); |
return; |
} |
|
// interpret the condition expression |
linei--; // make any error message apply to previous line |
interpretCondition(code); |
linei++; |
|
// make instruction code |
code.etype |= XPR_JUMPOS | XPR_SYM1; |
code.section = section; |
|
// make block record with label names |
block.blockType = HL_WHILE; // while-block |
block.blockNumber = ++iLoop; |
block.startBracket = tokenB; |
char name[32]; |
sprintf(name, "@while_%u_a", iLoop); |
uint32_t symi1 = makeLabelSymbol(name); |
block.jumpLabel = symbols[symi1].st_name; // label to jump back to |
sprintf(name, "@while_%u_b", iLoop); |
uint32_t symi2 = makeLabelSymbol(name); |
block.breakLabel = symbols[symi2].st_name; // label after loop. used if condition is false first time and for break statements |
block.continueLabel = 0xFFFFFFFF; // this label will only be made if there is a continue statement |
|
// make code to check condition before first iteration |
SCode code1 = code; |
invertCondition(code1); // invert condition to jump if false |
|
if (code1.instruction == (II_JUMP | II_JUMP_INVERT)) { |
// constant: don't jump |
code1.instruction = 0; |
} |
code1.sym5 = block.breakLabel; |
// check if it can be merged with previous instruction |
mergeJump(code1); |
// finish code and store it |
checkCode1(code1); |
if (lineError) return; |
fitCode(code1); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(code1);// save code structure |
|
// make loop label |
zeroAllMembers(code1); |
code1.label = block.jumpLabel; |
code1.section = section; |
codeBuffer.push(code1);// save code structure |
|
// make instruction to place at end of loop |
code.sym5 = block.jumpLabel; |
checkCode1(code); |
// store in codeBuffer2 for later insertion at the end of the loop |
block.codeBuffer2index = codeBuffer2.push(code); |
block.codeBuffer2num = 1; |
|
// store block in hllBlocks stack. will be retrieved at matching '}' |
hllBlocks.push(block); |
} |
|
// Finish while-loop at end bracket |
void CAssembler::codeWhile2() { |
SCode code; // code record for jump back |
SBlock block = hllBlocks.pop(); // pop the stack of {} blocks |
if (block.continueLabel != 0xFFFFFFFF) { |
// place label here as jump target for continue statements |
zeroAllMembers(code); |
code.label = block.continueLabel; |
code.section = section; |
codeBuffer.push(code); |
} |
|
uint32_t codebuf2num = codeBuffer2.numEntries(); |
if (block.codeBuffer2num && block.codeBuffer2index < codebuf2num) { |
// retrieve jumpback instruction |
code = codeBuffer2[block.codeBuffer2index]; |
|
if (code.instruction == (II_JUMP | II_JUMP_INVERT)) { |
// constant: don't jump |
code.instruction = 0; |
} |
// check if it can be merged with previous instruction |
mergeJump(code); |
// finish code and store it |
checkCode1(code); |
if (lineError) return; |
fitCode(code); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(code); // save code |
// remove from temporary buffer |
if (block.codeBuffer2index + 1 == codebuf2num) codeBuffer2.pop(); |
// place label for breaking out |
zeroAllMembers(code); |
code.label = block.breakLabel; |
code.section = section; |
codeBuffer.push(code); // save label |
return; |
} |
} |
|
// Interpret do-while loop in assembly code |
void CAssembler::codeDo() { |
SBlock block; // block record |
SCode code; // code record for label |
zeroAllMembers(block); |
zeroAllMembers(code); |
|
// make block record with label names |
block.blockType = HL_DO_WHILE; // do-while-block |
block.blockNumber = ++iLoop; |
char name[32]; |
sprintf(name, "@do_%u_a", iLoop); |
uint32_t symi1 = makeLabelSymbol(name); |
block.jumpLabel = symbols[symi1].st_name; // label to jump back to |
block.breakLabel = 0xFFFFFFFF; // this label will only be made if there is a break statement |
block.continueLabel = 0xFFFFFFFF; // this label will only be made if there is a continue statement |
// make loop label |
code.label = block.jumpLabel; |
code.section = section; |
codeBuffer.push(code); // save label |
|
// get next line with '{' |
if (linei == lines.numEntries()-1) { // no more lines |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
lineError = false; |
// expect '{' |
if (tokens[tokenB].id != '{') { |
errors.report(tokens[tokenB]); |
return; |
} |
block.startBracket = tokenB; |
hllBlocks.push(block); // store block in hllBlocks stack. will be retrieved at matching '}' |
} |
|
// Finish do-while loop at end bracket |
void CAssembler::codeDo2() { |
SCode code; // code record |
|
SBlock block = hllBlocks.pop(); // pop the stack of {} blocks |
if (block.continueLabel != 0xFFFFFFFF) { |
// place label here as jump target for continue statements |
zeroAllMembers(code); |
code.label = block.continueLabel; |
code.section = section; |
codeBuffer.push(code); |
} |
// find 'while' keyword in next pseudo-line after '}' |
if (linei+1 >= lines.numEntries()) { // no more lines |
errors.reportLine(ERR_WHILE_EXPECTED); return; |
} |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
lineError = false; |
|
// expect "while (expression)" |
uint32_t state = 0; // 0: start, 1: after type, 2: after 'while', 3: after (, 4: after (type, |
// 5: after expression, 6: after ')' |
uint32_t tok; // current token index |
SToken token; // current token |
SExpression expr; // expression in () |
zeroAllMembers(code); // reset code |
|
// interpret line by state machine looping through tokens |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
if (lineError) return; |
token = tokens[tok]; |
|
switch (state) { |
case 0: // start. expect type or 'while' |
if (token.type == TOK_TYP) { |
code.dtype = dataType = token.id & 0xFF; |
state = 1; |
} |
else if (token.id == HLL_WHILE) state = 2; |
else errors.reportLine(ERR_WHILE_EXPECTED); |
break; |
case 1: // after type. expect '+' or 'while' |
if (token.type == TOK_OPR && token.id == '+') { |
code.dtype |= TYP_PLUS; |
} |
else if (token.id == HLL_WHILE) state = 2; |
else errors.report(token); |
break; |
case 2: // after 'while'. expect '(' |
if (token.type == TOK_OPR && token.id == '(') state = 3; |
else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS); |
break; |
case 3: // after '('. expect type or logical expression |
if (token.type == TOK_TYP && !code.dtype) { |
code.dtype = dataType = token.id & 0xFF; |
state = 4; |
break; |
} |
EXPRESSION: |
expr = expression(tok, tokenB + tokenN - tok, (code.dtype & TYP_UNS) != 0); |
if (lineError) return; |
|
// insert logical expression into block |
insertAll(code, expr); |
tok += expr.tokens - 1; |
state = 5; |
break; |
case 4: // after "while (type". expect '+' or expression |
if (token.type == TOK_OPR && token.id == '+') { |
code.dtype |= TYP_PLUS; |
break; |
} |
// not a '+'. expect expression |
goto EXPRESSION; |
case 5: // after expression. expect ')' |
if (token.type == TOK_OPR && token.id == ')') state = 6; |
else { |
errors.report(token); return; |
} |
break; |
} |
} |
// should end at state 6 |
if (state != 6) errors.report(token); |
if (lineError) return; |
|
// make instruction with condition |
interpretCondition(code); |
code.etype |= XPR_JUMPOS | XPR_SYM1; |
code.section = section; |
code.sym5 = block.jumpLabel; |
|
if (code.instruction == (II_JUMP | II_JUMP_INVERT)) { |
// constant: don't jump |
code.instruction = 0; |
} |
// check if it can be merged with previous instruction |
mergeJump(code); |
// finish code and fit it |
checkCode1(code); |
if (lineError) return; |
fitCode(code); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(code);// save code structure |
|
if (block.breakLabel != 0xFFFFFFFF) { |
// place label here as jump target for break statements |
zeroAllMembers(code); |
code.label = block.breakLabel; |
code.section = section; |
codeBuffer.push(code); |
} |
} |
|
// Interpret for-loop in assembly code |
void CAssembler::codeFor() { |
uint32_t tok; // token number |
|
// search for 'in' keyword |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
if (tokens[tok].type == TOK_HLL && tokens[tok].id == HLL_IN) { |
// this is a for-in vector loop |
codeForIn(); |
return; |
} |
} |
|
// this is a traditional for(;;) loop |
uint32_t state = 0; // state while interpreting line |
// 0: start, 1: after type, 2: after 'for', 3: after (, 4: after (type, |
SBlock block; // block descriptor to save |
zeroAllMembers(block); // reset |
block.blockType = HL_FOR; // 'for' block |
block.breakLabel = block.jumpLabel = block.continueLabel = 0xFFFFFFFF; |
SToken token; // current token |
SToken typeToken; // token defining type |
// uint32_t type = 0; // operand type for all three expressions in for(;;) |
dataType = 0; // operand type for all three expressions in for(;;) |
uint32_t symi = 0; // symbol index |
char name[48]; // symbol name |
int conditionFirst = 0; // evaluation of the condition before first iteration: |
// 0: condition must be checked before first iteration |
// 2: condition is false before first iteration. zero iterations |
// 3: condition is true before first iteration. no initial check needed |
|
// interpret line by state machine looping through tokens |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
if (lineError) break; |
token = tokens[tok]; |
|
if (state == 0) { // start. expect type or 'for' |
if (token.type == TOK_TYP) { |
dataType = token.id & 0xFF; |
typeToken = token; |
state = 1; |
} |
else if (token.id == HLL_FOR) state = 2; |
else errors.report(token); |
} |
else if (state == 1) { // after type. expect '+' or 'for' |
if (token.type == TOK_OPR && token.id == '+') { |
dataType |= TYP_PLUS; |
} |
else if (token.id == HLL_FOR) state = 2; |
else errors.report(token); |
} |
else if (state == 2) { // after 'for'. expect '(' |
if (token.type == TOK_OPR && token.id == '(') state = 3; |
else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS); |
} |
else if (state == 3) { // after '('. expect type or initialization |
if (token.type == TOK_TYP && !dataType) { |
dataType = token.id & 0xFF; |
typeToken = token; |
tok++; |
if (tok < tokenB + tokenN && tokens[tok].type == TOK_OPR && tokens[tok].id == '+') { |
// '+' after type |
dataType |= TYP_PLUS; |
tok++; |
} |
} |
state = 4; |
break; // end tok loop here |
} |
} |
if (state != 4) { |
errors.report(token); |
return; |
} |
if (lineError) return; |
|
// check type |
if (dataType == 0) { |
errors.reportLine(ERR_TYPE_MISSING); return; |
} |
// extend type to int32 if allowed. this allows various optimizations |
// (unsigned types not allowed, even if start and end values are known to be positive, because loop counter may become negative inside loop in rare cases) |
if ((dataType & TYP_PLUS) && ((dataType & 0xFF) < (TYP_INT32 & 0xFF))) dataType = TYP_INT32; |
|
// remake token sequence for generating initial instruction |
uint32_t tokensRestorePoint = tokens.numEntries(); |
typeToken.id = dataType; |
tokens.push(typeToken); |
uint32_t tokenFirst = tok; |
if (tokens[tokenFirst].type == TOK_TYP) tokenFirst++; // skip type token. it has already been inserted |
for (; tok < tokenB + tokenN; tok++) { // insert remaining tokens |
token = tokens[tok]; |
if (token.type == TOK_OPR && token.id == ';') break; |
tokens.push(tokens[tok]); |
} |
// make an instruction out of this sequence |
tokenB = tokensRestorePoint; |
tokenN = tokens.numEntries() - tokensRestorePoint; |
uint32_t codePoint = codeBuffer.numEntries(); |
SCode initializationCode; |
zeroAllMembers(initializationCode); |
if (tokenN > 1) { // skip if there is no code |
interpretCodeLine(); |
if (codeBuffer.numEntries() == codePoint + 1) { |
// remember initialization code |
initializationCode = codeBuffer[codePoint]; |
} |
} |
// remove temporary token sequence |
tokens.setNum(tokensRestorePoint); |
if (lineError) return; |
|
// get next line containing loop condition |
SCode conditionCode; |
zeroAllMembers(conditionCode); |
conditionCode.section = section; |
if (linei+2 >= lines.numEntries()) { // no more lines |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
if (tokenN == 1 && tokens[tokenB].type == TOK_OPR && tokens[tokenB].id == ';') { |
// no condition specified. infinite loop |
conditionFirst = 3; |
conditionCode.instruction = II_JUMP; |
conditionCode.etype = XPR_JUMPOS; |
} |
else { |
SExpression expr = expression(tokenB, tokenN, (dataType & TYP_UNS) != 0); |
if (lineError) return; |
insertAll(conditionCode, expr); // insert logical expression into block |
conditionCode.dtype = dataType; |
interpretCondition(conditionCode); // convert expression to conditional jump |
if (conditionCode.etype == XPR_INT) { // always true or always false |
conditionFirst = 2 + (conditionCode.value.w & 1); |
conditionCode.instruction = II_JUMP; |
conditionCode.etype = XPR_JUMPOS; |
conditionCode.value.i = 0; |
conditionCode.dtype = 0; |
} |
else { |
conditionCode.etype |= XPR_JUMPOS | XPR_SYM1; |
conditionCode.section = section; |
tok = tokenB + expr.tokens; // expect ';' after expression |
if (tokens[tok].type != TOK_OPR || tokens[tok].id != ';') { |
errors.report(tokens[tok]); |
} |
//uint32_t counterRegister = 0; |
uint64_t counterStart = 0; |
// are start and end values known constants? |
if (initializationCode.instruction == II_MOVE && (initializationCode.etype & XPR_INT) && initializationCode.dest |
&& !(initializationCode.etype & (XPR_REG1 | XPR_MEM | XPR_OPTION))) { |
//counterRegister = initializationCode.dest; |
counterStart = initializationCode.value.i; |
if ((expr.etype & XPR_INT) && (expr.etype & XPR_REG1) && !(expr.etype & (XPR_REG2 | XPR_MEM | XPR_OPTION))) { |
// start and end values are integer constants |
if ((expr.instruction & 0xFF) == II_COMPARE) { |
// compare instruction. condition is in option bits |
switch ((expr.optionbits >> 1) & 3) { |
case 0: // == |
conditionFirst = 2 + (counterStart == expr.value.u); break; |
case 1: // < |
if (dataType & TYP_UNS) conditionFirst = 2 + (counterStart < expr.value.u); |
else conditionFirst = 2 + ((int64_t)counterStart < expr.value.i); |
break; |
case 2: // > |
if (dataType & TYP_UNS) conditionFirst = 2 + (counterStart > expr.value.u); |
else conditionFirst = 2 + ((int64_t)counterStart > expr.value.i); |
break; |
} |
if (expr.optionbits & 1) conditionFirst ^= 1; // invert if bit 0 |
} |
else if ((expr.instruction & 0xFF) == II_AND) { |
// bit test. if (r1 & 1 << n) |
conditionFirst = 2 + ((counterStart & ((uint64_t)1 << expr.value.u)) != 0); |
} |
} |
} |
} |
} |
|
// make block record with label name |
block.blockNumber = ++iLoop; |
|
if (conditionFirst == 0) { |
// condition must be checked before first iteration |
invertCondition(conditionCode); // invert condition |
sprintf(name, "@for_%u_b", iLoop); |
symi = makeLabelSymbol(name); |
block.breakLabel = symbols[symi].st_name; |
conditionCode.sym5 = block.breakLabel; |
// check if it can be merged with previous instruction |
mergeJump(conditionCode); |
checkCode1(conditionCode); // finish code and fit it |
if (lineError) return; |
fitCode(conditionCode); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(conditionCode); // save code structure |
invertCondition(conditionCode); // invert condition back again |
} |
else if (conditionFirst == 2) { |
// condition is known to be false. loop goes zero times |
SCode jumpAlways; |
zeroAllMembers(jumpAlways); |
jumpAlways.instruction = II_JUMP; // jump past loop |
jumpAlways.section = section; |
jumpAlways.etype = XPR_JUMPOS; |
sprintf(name, "@for_%u_goes_zero_times", iLoop); |
symi = makeLabelSymbol(name); |
block.breakLabel = symbols[symi].st_name; |
jumpAlways.sym5 = block.breakLabel; |
// check if it can be merged with previous instruction |
mergeJump(jumpAlways); |
checkCode1(jumpAlways); // finish code and fit it |
if (lineError) return; |
fitCode(jumpAlways); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(jumpAlways); // save code structure |
} |
// make label for loop back |
if (conditionCode.instruction != II_JUMP) { |
sprintf(name, "@for_%u_a", iLoop); |
} |
else { |
sprintf(name, "@infinite_loop_%u_a", iLoop); |
} |
symi = makeLabelSymbol(name); |
block.jumpLabel = symbols[symi].st_name; |
conditionCode.sym5 = block.jumpLabel; |
SCode codeLabel; |
zeroAllMembers(codeLabel); |
codeLabel.label = block.jumpLabel; |
codeLabel.section = section; |
codeBuffer.push(codeLabel); |
|
// get next line containing increment |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
// line must end with ')' |
if (tokenN < 1) { |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
if (tokens[tokenB+tokenN-1].type != TOK_OPR || tokens[tokenB+tokenN-1].id != ')') { |
errors.report(tokens[tokenB+tokenN-1]); // expecting ')' |
return; |
} |
|
// make instruction for loop counter increment |
tokens.push(typeToken); |
for (tok = tokenB; tok < tokenB + tokenN - 1; tok++) { // insert remaining tokens |
tokens.push(tokens[tok]); |
} |
// make an instruction out of this sequence |
tokenB = tokensRestorePoint; |
tokenN = tokens.numEntries() - tokensRestorePoint; |
SCode incrementCode; |
zeroAllMembers(incrementCode); |
codePoint = codeBuffer.numEntries(); |
if (tokenN > 1) { |
interpretCodeLine(); |
if (codeBuffer.numEntries() == codePoint + 1) { |
incrementCode = codeBuffer[codePoint]; |
incrementCode.section = section; |
} |
} |
// remove temporary token sequence |
tokens.setNum(tokensRestorePoint); |
// remove temporary incrementation code. it has to be inserted after the loop |
codeBuffer.setNum(codePoint); |
if (lineError) return; |
|
// save instructions in block |
block.codeBuffer2index = codeBuffer2.push(incrementCode); |
codeBuffer2.push(conditionCode); |
block.codeBuffer2num = 2; |
|
// get next line containing '{' |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
// line must contain '{' |
if (tokenN != 1 || tokens[tokenB].type != TOK_OPR || tokens[tokenB].id != '{') { |
errors.reportLine(ERR_EXPECT_BRACKET); |
return; |
} |
block.startBracket = tokenB; |
|
// save block to be recalled at end bracket |
hllBlocks.push(block); |
|
} |
|
// Finish for-loop at end bracket |
void CAssembler::codeFor2() { |
SCode incrementCode; // code record for incrementing loop counter |
SCode conditionCode; // code record for conditional jump back |
SCode labelCode; // code record for label |
SBlock block = hllBlocks.pop(); // pop the stack of {} blocks |
if (block.continueLabel != 0xFFFFFFFF) { |
// place label here as jump target for continue statements |
zeroAllMembers(labelCode); |
labelCode.label = block.continueLabel; |
labelCode.section = section; |
codeBuffer.push(labelCode); |
} |
|
uint32_t codebuf2num = codeBuffer2.numEntries(); |
if (block.codeBuffer2num == 2 && block.codeBuffer2index < codebuf2num) { |
// retrieve prepared instruction |
incrementCode = codeBuffer2[block.codeBuffer2index]; |
conditionCode = codeBuffer2[block.codeBuffer2index+1]; |
|
// finish increment code and store it |
if (incrementCode.instruction) { |
checkCode1(incrementCode); |
if (lineError) return; |
fitCode(incrementCode); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(incrementCode); // save code |
} |
|
// finish condition code and store it |
// check if it can be merged with previous instruction |
mergeJump(conditionCode); |
checkCode1(conditionCode); |
if (lineError) return; |
fitCode(conditionCode); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(conditionCode); // save code |
|
// remove from temporary buffer |
if (block.codeBuffer2index + 2 == codebuf2num) { |
codeBuffer2.pop(); codeBuffer2.pop(); |
} |
// place label for breaking out |
if (block.breakLabel != 0xFFFFFFFF) { |
zeroAllMembers(labelCode); |
labelCode.label = block.breakLabel; |
labelCode.section = section; |
codeBuffer.push(labelCode); // save label |
} |
} |
} |
|
// Interpret for-in vector loop in assembly code |
// for (float v1 in [r1-r2]) {} |
void CAssembler::codeForIn() { |
uint32_t state = 0; // state while interpreting line |
// 0: start, 1: after type, 2: after 'for', 3: after (, 4: after (type, |
// 5: after vector register, 6: after 'in', 7: after '[' |
// 8: after base register, 9: after '-', 10: after index register |
// 11: after ']', 12: after ')' |
SBlock block; // block descriptor to save |
zeroAllMembers(block); // reset |
block.blockType = HL_FOR_IN; // 'for-in' block |
block.breakLabel = block.jumpLabel = block.continueLabel = 0xFFFFFFFF; |
block.blockNumber = ++iLoop; // number to use in labels |
//uint32_t baseReg; // base register |
uint32_t indexReg = 0; // index register |
uint32_t tok; // token number |
SToken token; // current token |
uint32_t type = 0; // vector element type |
uint32_t symi; // symbol index |
char name[32]; // symbol name |
|
// interpret line by state machine looping through tokens |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
if (lineError) break; |
token = tokens[tok]; |
|
switch (state) { |
case 0: // start. expect type or 'for' |
if (token.type == TOK_TYP) { |
type = token.id & 0xFF; |
state = 1; |
} |
else if (token.type == TOK_HLL && token.id == HLL_FOR) { |
state = 2; |
} |
else errors.report(token); |
break; |
case 1: // after type. expect 'for' ('+' not allowed after type) |
if (token.type == TOK_HLL && token.id == HLL_FOR) { |
state = 2; |
} |
else errors.report(token); |
break; |
case 2: // after 'for'. expect '(' |
if (token.type == TOK_OPR && token.id == '(') { |
state = 3; |
} |
else errors.report(token); |
break; |
case 3: // after '('. expect type or vector register |
if (token.type == TOK_TYP && !type) { |
type = token.id & 0xFF; |
state = 4; |
} |
else if (token.type == TOK_REG) { |
// must be vector register |
if (!(token.id & REG_V)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE); |
state = 5; |
} |
else errors.report(token); |
break; |
case 4: // after type. expect vector register |
if (token.type == TOK_REG) { |
// must be vector register |
if (!(token.id & REG_V)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE); |
state = 5; |
} |
else errors.report(token); |
break; |
case 5: // after vector register. expect 'in' |
if (token.type == TOK_HLL && token.id == HLL_IN) { |
state = 6; |
} |
else errors.report(token); |
break; |
case 6: // after 'in'. expect '[' |
if (token.type == TOK_OPR && token.id == '[') { |
state = 7; |
} |
else errors.report(token); |
break; |
case 7: // after '['. expect base register |
if (token.type == TOK_REG) { |
// must be general purpose register |
if (!(token.id & REG_R)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE); |
//baseReg = token.id; |
state = 8; |
} |
else errors.report(token); |
break; |
case 8: // after base register. expect '-' |
if (token.type == TOK_OPR && token.id == '-') { |
state = 9; |
} |
else errors.report(token); |
break; |
case 9: // after '-'. expect index register |
if (token.type == TOK_REG) { |
// must be general purpose register, except r31 |
if (!(token.id & REG_R) || token.id == (REG_R|31)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE); |
indexReg = token.id; |
state = 10; |
} |
else errors.report(token); |
break; |
case 10: // after index register. expect ']' |
if (token.type == TOK_OPR && token.id == ']') { |
state = 11; |
} |
else errors.report(token); |
break; |
case 11: // after ']'. expect ')' |
if (token.type == TOK_OPR && token.id == ')') { |
state = 12; |
} |
else errors.report(token); |
break; |
default: |
errors.report(token); |
} |
} |
// get next line and expect '{' |
if (linei == lines.numEntries()-1) { // no more lines |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
linei++; |
tokenB = lines[linei].firstToken; // first token in line |
tokenN = lines[linei].numTokens; // number of tokens in line |
lineError = false; |
|
// expect '{' |
if (tokens[tokenB].id != '{') { |
errors.reportLine(ERR_EXPECT_BRACKET); |
return; |
} |
block.startBracket = tokenB; |
|
// look at preceding instruction to see if value of index register is known to be positive |
bool startCheckNeeded = true; |
SCode previousInstruction; |
if (codeBuffer.numEntries()) { |
previousInstruction = codeBuffer[codeBuffer.numEntries()-1]; // recall previous instruction |
if (previousInstruction.section == section && previousInstruction.instruction == II_MOVE |
&& (previousInstruction.etype & XPR_INT) && previousInstruction.dest == indexReg |
&& !(previousInstruction.etype & (XPR_REG1 | XPR_MEM | XPR_OPTION)) |
&& previousInstruction.value.i > 0) { |
startCheckNeeded = false; |
} |
} |
if (startCheckNeeded) { |
// make label name for break |
sprintf(name, "@for_%u_b", iLoop); |
symi = makeLabelSymbol(name); |
block.breakLabel = symbols[symi].st_name; // label to jump back to |
// make conditional jump if index not positive |
SCode startCheck; |
zeroAllMembers(startCheck); |
startCheck.section = section; |
startCheck.instruction = II_COMPARE | II_JUMP_POSITIVE | II_JUMP_INVERT; |
startCheck.reg1 = indexReg; |
startCheck.sym5 = block.breakLabel; |
startCheck.etype = XPR_INT | XPR_REG | XPR_REG1 | XPR_JUMPOS; |
startCheck.line = linei; |
startCheck.dtype = TYP_INT64; |
mergeJump(startCheck); // check if it can be merged with previous instruction |
checkCode1(startCheck); |
fitCode(startCheck); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(startCheck); // save instruction |
} |
// make label name for loop |
sprintf(name, "@for_%u_a", iLoop); |
symi = makeLabelSymbol(name); |
block.jumpLabel = symbols[symi].st_name; // label to jump back to |
SCode labelCode; |
zeroAllMembers(labelCode); |
labelCode.section = section; |
labelCode.label = block.jumpLabel; |
codeBuffer.push(labelCode); // save label |
|
// save index registr and type in block |
block.codeBuffer2num = indexReg; |
block.codeBuffer2index = type; |
|
// save block to be recalled at '}' |
hllBlocks.push(block); |
} |
|
// Finish for-in vector loop in assembly code |
void CAssembler::codeForIn2() { |
SCode code; // code record for jump back |
SBlock block = hllBlocks.pop(); // pop the stack of {} blocks |
if (block.continueLabel != 0xFFFFFFFF) { |
// place label here as jump target for continue statements |
zeroAllMembers(code); |
code.label = block.continueLabel; |
code.section = section; |
codeBuffer.push(code); |
} |
// make loop instruction |
zeroAllMembers(code); |
code.section = section; |
code.line = linei; |
code.instruction = II_SUB_MAXLEN | II_JUMP_POSITIVE; |
code.reg1 = code.dest = block.codeBuffer2num; |
code.value.u = block.codeBuffer2index & 0xF; // element type in vector |
code.dtype = TYP_INT64; |
code.sym5 = block.jumpLabel; |
code.etype = XPR_INT | XPR_REG | XPR_REG1 | XPR_JUMPOS; |
// fit code and save it |
checkCode1(code); |
fitCode(code); |
if (lineError) return; |
codeBuffer.push(code); // save instruction |
// make break label |
if (block.breakLabel != 0xFFFFFFFF) { |
zeroAllMembers(code); |
code.section = section; |
code.label = block.breakLabel; |
codeBuffer.push(code); // save label |
} |
} |
|
|
// Interpret switch statement in assembly code |
void CAssembler::codeSwitch() { |
/* |
CMetaBuffer<CDynamicArray<SCcaseLabel> > caseLabels; |
CDynamicArray<SCode> extraCode; |
if (caseLabels.numEntries() == 0) caseLabels.setNum(switchNumber); |
*/ |
|
|
} |
|
// Interpret switch case label in assembly code |
void CAssembler::codeCase() {} |
|
// Finish switch statement at end bracket |
void CAssembler::codeSwitch2() { |
} |
|
// Interpret break or continue statement in assembly code |
void CAssembler::codeBreak() { |
uint32_t symi = findBreakTarget(tokens[tokenB].id); |
if (symi == 0) { |
errors.report(tokens[tokenB].pos, tokens[tokenB].stringLength, tokens[tokenB].id == HLL_BREAK ? ERR_MISPLACED_BREAK : ERR_MISPLACED_CONTINUE); |
return; |
} |
// make jump to symi |
SCode code; |
zeroAllMembers(code); |
code.section = section; |
code.instruction = II_JUMP; |
code.etype = XPR_JUMPOS | XPR_SYM1; |
code.sym5 = symi; |
|
// check if it can be merged with previous instruction |
mergeJump(code); |
|
// finish code and save it |
checkCode1(code); |
if (lineError) return; |
fitCode(code); // find an instruction variant that fits |
if (lineError) return; |
codeBuffer.push(code);// save code structure |
} |
|
|
// Find or make the target symbol of a break or continue statement |
uint32_t CAssembler::findBreakTarget(uint32_t k) { |
// k is HLL_BREAK or HLL_CONTINUE |
int32_t blocki; // block index |
uint32_t symi; // symbol id |
bool found = false; // target found |
// search backwords through blocks to find the first block that can have break/continue |
for (blocki = (int32_t)hllBlocks.numEntries() - 1; blocki >= 0; blocki--) { |
switch (hllBlocks[blocki].blockType) { |
case HL_FOR: case HL_FOR_IN: case HL_WHILE: case HL_DO_WHILE: |
// can have break and continue |
found = true; |
break; |
case HL_SWITCH: |
// can have break, but not continue |
if (k == HLL_BREAK) found = true; |
break; |
case HL_FUNC: case HL_SECTION: |
// stop search and fail |
return 0; |
} |
if (found) break; |
} |
if (!found) return 0; // not found |
char prefix; |
// find symbol in block |
if (k == HLL_BREAK) { |
symi = hllBlocks[blocki].breakLabel; |
prefix = 'b'; |
} |
else { |
symi = hllBlocks[blocki].continueLabel; |
prefix = 'c'; |
} |
if (symi != 0xFFFFFFFF) return symi; // symbol has already been assigned |
|
// symbol has not been assigned yet. give it a name |
const char * blockName = 0; |
switch (hllBlocks[blocki].blockType) { |
case HL_FOR: case HL_FOR_IN: |
blockName = "for"; break; |
case HL_WHILE: |
blockName = "while"; break; |
case HL_DO_WHILE: |
blockName = "do"; break; |
case HL_SWITCH: |
blockName = "switch"; break; |
default: return 0; |
} |
char name[32]; |
sprintf(name, "@%s_%u_%c", blockName, hllBlocks[blocki].blockNumber, prefix); |
symi = makeLabelSymbol(name); |
symi = symbols[symi].st_name; |
// save name in block, in case it is needed again |
if (k == HLL_BREAK) { |
hllBlocks[blocki].breakLabel = symi; |
} |
else { |
hllBlocks[blocki].continueLabel = symi; |
} |
return symi; |
} |
|
|
// Make a symbol for branch label etc., address not known yet. Returns zero if already defined |
uint32_t CAssembler::makeLabelSymbol(const char * name) { |
ElfFWC_Sym2 sym; |
zeroAllMembers(sym); |
sym.st_type = STT_FUNC; |
sym.st_other = STV_HIDDEN | STV_IP; |
sym.st_section = section; |
sym.st_name = symbolNameBuffer.putStringN(name, (uint32_t)strlen(name)); |
uint32_t symi = addSymbol(sym); // save symbol with name |
if (symi == 0) { |
errors.reportLine(ERR_SYMBOL_DEFINED); |
} |
return symi; |
} |
|
// Merge jump instruction with preceding arithmetic instruction. |
// If successful, returns true and puts the result in code1 |
bool CAssembler::mergeJump(SCode & code2) { |
if (cmd.optiLevel == 0) return false; // merge only if optimization is on |
if (code2.label) return false; // cannot merge if there is a label between the two instructions |
if (codeBuffer.numEntries() == 0) return false; // no previous instruction to merge with |
SCode code1 = codeBuffer[codeBuffer.numEntries()-1]; // previous code |
|
if (code1.section != code2.section) return false; // must be in same section |
SCode code3 = code1 | code2; // combined code |
code3.reg1 = code1.reg1; |
code3.dest = code1.dest; |
uint32_t type = code1.dtype; |
// first instruction cannot have memory operand or other special options |
if (code1.etype & (XPR_MEM | XPR_SYM1 | XPR_MASK | XPR_OPTION | XPR_OPTIONS | XPR_JUMPOS | XPR_ERROR)) return false; |
if (!(code2.etype & XPR_JUMPOS)) return false; |
|
/*if (code2.instruction == II_JUMP) { |
// combine unconditional jump with add/sub |
if (code2.etype & (XPR_MEM | XPR_OPTION | XPR_REG)) return false; |
if ((code1.instruction & ~1) != II_ADD) return false; // only add and sub |
if (type & TYP_FLOAT) return false; |
// successful combination of add/sub and jump |
codeBuffer.pop(); // remove previous code from buffer |
code2 = code3; |
return true; |
} */ |
|
// second instruction must test the result of the first instruction |
if (code1.dest != code2.reg1) return false; |
// must have same operand type |
if ((code1.dtype & 0xF) > (code2.dtype & 0xF)) { |
if (!(code2.dtype & TYP_PLUS)) return false; |
} |
if ((code1.dtype & 0xF) < (code2.dtype & 0xF)) { |
if (!(code1.dtype & TYP_PLUS)) return false; |
type = code2.dtype; |
} |
type |= code2.dtype & TYP_UNS; // signed/unsigned distinction only relevant for second instruction |
code3.dtype = type; |
|
// check if constant bigger than 32 bits |
if ((type & XPR_INT) && !(type & TYP_UNS) && (code1.value.i < (int32_t)0x80000000 || code1.value.i > (int32_t)0x7FFFFFFF)) return false; |
if ((type & XPR_INT) && (type & TYP_UNS) && (code1.value.u > (uint64_t)0xFFFFFFFF)) return false; |
if ((type & XPR_FLT) && (type & 0xFF) > TYP_FLOAT32) return false; |
|
switch (code1.instruction) { |
case II_ADD: case II_SUB: |
if (type & TYP_FLOAT) return false; |
if (code1.instruction == II_ADD && code1.value.u == 1 && !(type & TYP_UNS)) { |
// check if it fits increment_compare/jump below/above |
code3.value.u = code2.value.u; |
/* |
uint32_t nbits = (1 << (type & 7)) * 8; // number of bits in constant |
if ((code2.instruction & 0xFFFE00) == II_JUMP_NEGATIVE && (code2.etype & XPR_INT) |
&& (code3.value.u & (((uint64_t)1 << nbits) - 1)) != (uint64_t)1 << (nbits - 1)) { // check for overflow |
// convert jump_sbelow n to jump_sbeloweq n-1 |
// or jump_saboveeq n to jump sabove n-1 |
code3.value.u--; |
code3.instruction ^= II_JUMP_NEGATIVE ^ II_JUMP_POSITIVE ^ II_JUMP_INVERT; |
}*/ |
if ((code3.instruction & 0xFFFE00) == II_JUMP_POSITIVE || (code3.instruction & 0xFFFE00) == II_JUMP_NEGATIVE) { |
// successful combination of add 1 and jump_sbelow/above |
code3.instruction = (code3.instruction & 0xFFFF00) | II_INCREMENT; |
code3.etype = (code1.etype & ~XPR_IMMEDIATE) | code2.etype; |
codeBuffer.pop(); // remove previous code from buffer |
code2 = code3; |
return true; |
} |
} |
// add/sub + compare |
if (!(code2.etype & XPR_INT) || code2.value.i != 0 || (code2.instruction & 0xFF) != II_COMPARE) return false; // must compare against zero |
|
//??: |
if ((type & TYP_UNS) && (code3.instruction & 0xFFFE00) != II_JUMP_ZERO) return false; // unsigned works only for == and != |
// successful combination of add/sub and signed compare with zero |
code3.instruction = code1.instruction | (code2.instruction & 0xFFFF00); |
code3.etype = code1.etype | (code2.etype & ~(XPR_IMMEDIATE | XPR_OPTIONS)); |
codeBuffer.pop(); // remove previous code from buffer |
code2 = code3; |
return true; |
|
case II_AND: case II_OR: case II_XOR: |
//case II_SHIFT_LEFT: case II_SHIFT_RIGHT_U: |
// must compare for equal to zero |
if (!(code2.etype & XPR_INT) || code2.value.i != 0) return false; |
//if ((code2.instruction & 0xFFFE00) != II_JUMP_ZERO) return false; |
if ((code2.instruction & ~ II_JUMP_INVERT) != (II_JUMP_ZERO | II_COMPARE)) return false; |
// combine instruction codes |
code3.instruction = code1.instruction | (code2.instruction & 0xFFFF00); |
code3.etype = code1.etype | (code2.etype & ~XPR_IMMEDIATE); |
// successful combination of and/or/xor/shift and compare with zero |
codeBuffer.pop(); // remove previous code from buffer |
code2 = code3; |
return true; |
} |
// everything else fails |
return false; |
} |
|
// check if line contains unconditional direct jump and nothing else |
uint32_t CAssembler::hasJump(uint32_t line) { |
if (cmd.optiLevel == 0) return 0; // don't optimize jump if optimization is off |
if (line >= lines.numEntries()) return 0; // there is no line here |
uint32_t tokB = lines[line].firstToken; |
uint32_t tokN = lines[line].numTokens; // number of tokens in line |
if (tokN > 0 && tokens[tokB+tokN-1].type == TOK_OPR && tokens[tokB+tokN-1].id == ';') tokN--; // ignore ';' |
lineError = false; |
if (tokN == 1 && tokens[tokB].type == TOK_HLL) { // high level keyword |
if (tokens[tokB].id == HLL_BREAK || tokens[tokB].id == HLL_CONTINUE) { |
return findBreakTarget(tokens[tokB].id); // break or continue statement |
} |
} |
if (tokN == 2 && tokens[tokB].type == TOK_INS && tokens[tokB].id == II_JUMP && tokens[tokB+1].type == TOK_SYM) { |
// jump symbol |
// return tokens[tokB+1].value.w; |
return tokens[tokB+1].id; |
} |
return 0; // anything else |
} |
|
// interpret condition in if(), while(), and for(;;) statements |
void CAssembler::interpretCondition(SCode & code) { |
if ((code.instruction & 0xFF) == II_COMPARE) { |
// compare instruction. condition is in option bits |
switch ((code.optionbits >> 1) & 3) { |
case 0: |
code.instruction |= II_JUMP_ZERO; break; |
case 1: |
code.instruction |= (code.dtype & TYP_UNS) ? II_JUMP_CARRY : II_JUMP_NEGATIVE; break; |
case 2: |
code.instruction |= (code.dtype & TYP_UNS) ? II_JUMP_UABOVE : II_JUMP_POSITIVE; break; |
case 3: |
errors.reportLine(ERR_EXPECT_LOGICAL); // should not occur |
} |
if (code.optionbits & 1) code.instruction ^= II_JUMP_INVERT; // invert if bit 0 |
if (code.dtype & TYP_FLOAT) { // floating point. resolve ordered/unordered |
//if ((code.instruction & 0x7F00) == (II_JUMP_NOTZERO & 0x7F00)) code.optionbits |= 8; // a != b is unordered. This has already been done in CAssembler::op2Registers |
if ((code.optionbits & 8) && (code.instruction & 0x7F00) - 0x1000 < 0x2000) { |
code.instruction ^= II_JUMP_UNORDERED; // unordered bit |
} |
} |
} |
else if ((code.instruction & 0xFF) == II_AND && (code.etype & XPR_INT)) { |
// if (r1 & ) |
if (code.value.u != 0 && (code.value.u & (code.value.u-1)) == 0) { |
// n is a power of 2. test_bit |
code.instruction = II_TEST_BIT | II_JUMP_TRUE; |
code.value.u = bitScanReverse(code.value.u); |
} |
else { |
code.instruction = II_TEST_BITS_OR | II_JUMP_TRUE; |
} |
if (code.optionbits & 4) code.instruction ^= II_JUMP_INVERT; |
} |
else if ((code.instruction & 0xFF) == II_TEST_BITS_AND && (code.etype & XPR_INT)) { |
code.instruction |= II_JUMP_TRUE; |
if (code.optionbits & 1) code.instruction ^= II_JUMP_INVERT; |
} |
else if (code.instruction == 0 && code.etype == XPR_INT) { |
// constant condition. always true or always false |
code.instruction = II_JUMP; |
if (code.value.i == 0) code.instruction |= II_JUMP_INVERT; |
|
//!! |
code.etype = 0; |
} |
else { // unrecognized expression |
errors.reportLine(ERR_EXPECT_LOGICAL); |
code.instruction = II_JUMP; |
} |
code.optionbits = 0; |
} |
|
|
// push registers on stack |
void CAssembler::codePush() { |
uint32_t state = 0; // 0: begin, 1: after type, 2: after 'push', 3: after '(', |
// 4: after register 1, 5: after comma, 6: after register 2, 7: after comma, |
// 8: after constant, 9: after ')' |
int32_t reg1 = -1; // register 1, stack pointer if specified |
int32_t reg2 = -1; // register 2 |
uint32_t imm = -1; // immediate operand |
uint32_t ot = 3; // operand type |
uint32_t tok = 0; // token index |
SToken token; // token |
SCode code; // code structure |
zeroAllMembers(code); |
code.section = section; |
// loop through tokens on line |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
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; |
} |
|
switch (state) { |
case 0: // begin. expect type or 'push' |
if (token.id == HLL_PUSH) state = 2; |
else if (token.type == TOK_TYP) { |
ot = tokens[tok].id; |
state = 1; |
} |
else errors.report(token); |
break; |
|
case 1: // after type. expect 'push' |
if (token.id == HLL_PUSH) state = 2; |
else errors.report(token); |
break; |
|
case 2: // after 'push'. expect '(' |
if (token.type == TOK_OPR && token.id == '(') state = 3; |
else errors.report(token); |
break; |
|
case 3: // after '('. expect register |
if (token.type != TOK_REG) { |
errors.report(token); return; |
} |
state = 4; |
reg1 = token.id; |
break; |
|
case 4: // after register. expect ',' or ')' |
if (token.type == TOK_OPR && token.id == ',') state = 5; |
else if (token.type == TOK_OPR && token.id == ')') state = 9; |
else errors.report(token); |
break; |
|
case 5: // after comma. expect register or constant |
if (token.type == TOK_REG) { |
reg2 = token.id; state = 6; |
} |
else if (token.type == TOK_NUM || token.type == TOK_SYM) { |
imm = expression(tok, 1, 0).value.w; state = 8; |
} |
else errors.report(token); |
break; |
|
case 6: // after second register. expect comma or ')' |
if (token.type == TOK_OPR && token.id == ',') state = 7; |
else if (token.type == TOK_OPR && token.id == ')') state = 9; |
else errors.report(token); |
break; |
|
case 7: // after second comma. expect constant |
if (token.type == TOK_NUM || token.type == TOK_SYM) { |
imm = expression(tok, 1, 0).value.w; state = 8; |
} |
else errors.report(token); |
break; |
|
case 8: // after constant. expect ')' |
if (token.type == TOK_OPR && token.id == ')') state = 9; |
else errors.report(token); |
break; |
|
default: // after ')'. expect nothing |
errors.report(token); |
break; |
} |
} |
if (state != 9) { |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
if (reg2 == -1) { // stack pointer not specified. use default stack pointer |
reg2 = reg1; reg1 = 0x1F | REG_R; |
} |
if (int32_t(imm) == -1) { // no immediate operand. push only one register |
imm = reg2 & 0x1F; |
} |
if ((imm & 0x1F) < uint32_t(reg2 & 0x1F)) { |
errors.reportLine(ERR_OPERANDS_WRONG_ORDER); |
return; |
} |
if (!(reg1 & REG_R)) { |
errors.reportLine(ERR_WRONG_OPERANDS); |
return; |
} |
if ((reg2 & REG_V) && (imm & 0x80)) { |
errors.reportLine(ERR_WRONG_OPERANDS); |
return; |
} |
code.instruction = II_PUSH; |
code.dest = reg1; |
code.reg1 = reg2; |
code.value.u = imm; |
code.etype = XPR_INT | XPR_REG | XPR_REG1; |
code.dtype = TYP_INT8 | (ot & 0xF); |
checkCode1(code); |
fitCode(code); |
if (lineError) return; |
codeBuffer.push(code); // save code |
} |
|
|
// pop register from stack |
void CAssembler::codePop() { |
uint32_t state = 0; // 0: begin, 1: after type, 2: after 'push', 3: after '(', |
// 4: after register 1, 5: after comma, 6: after register 2, 7: after comma, |
// 8: after constant, 9: after ')' |
int32_t reg1 = -1; // register 1, stack pointer if specified |
int32_t reg2 = -1; // register 2 |
uint32_t imm = -1; // immediate operand |
uint32_t ot = 3; // operand type |
uint32_t tok = 0; // token index |
SToken token; // token |
SCode code; // code structure |
zeroAllMembers(code); |
code.section = section; |
// loop through tokens on line |
for (tok = tokenB; tok < tokenB + tokenN; tok++) { |
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; |
} |
switch (state) { |
case 0: // begin. expect type or 'push' |
if (token.id == HLL_POP) state = 2; |
else if (token.type == TOK_TYP) { |
ot = tokens[tok].id; |
state = 1; |
} |
else errors.report(token); |
break; |
|
case 1: // after type. expect 'push' |
if (token.id == HLL_POP) state = 2; |
else errors.report(token); |
break; |
|
case 2: // after 'push'. expect '(' |
if (token.type == TOK_OPR && token.id == '(') state = 3; |
else errors.report(token); |
break; |
|
case 3: // after '('. expect register |
if (token.type != TOK_REG) { |
errors.report(token); return; |
} |
state = 4; |
reg1 = token.id; |
break; |
|
case 4: // after register. expect ',' or ')' |
if (token.type == TOK_OPR && token.id == ',') state = 5; |
else if (token.type == TOK_OPR && token.id == ')') state = 9; |
else errors.report(token); |
break; |
|
case 5: // after comma. expect register or constant |
if (token.type == TOK_REG) { |
reg2 = token.id; state = 6; |
} |
else if (token.type == TOK_NUM || token.type == TOK_SYM) { |
imm = expression(tok, 1, 0).value.w; state = 8; |
} |
else errors.report(token); |
break; |
|
case 6: // after second register. expect comma or ')' |
if (token.type == TOK_OPR && token.id == ',') state = 7; |
else if (token.type == TOK_OPR && token.id == ')') state = 9; |
else errors.report(token); |
break; |
|
case 7: // after second comma. expect constant |
if (token.type == TOK_NUM || token.type == TOK_SYM) { |
imm = expression(tok, 1, 0).value.w; state = 8; |
} |
else errors.report(token); |
break; |
|
case 8: // after constant. expect ')' |
if (token.type == TOK_OPR && token.id == ')') state = 9; |
else errors.report(token); |
break; |
|
default: // after ')'. expect nothing |
errors.report(token); |
break; |
} |
} |
if (state != 9) { |
errors.reportLine(ERR_UNFINISHED_INSTRUCTION); |
return; |
} |
if (reg2 == -1) { // stack pointer not specified. use default stack pointer |
reg2 = reg1; reg1 = 0x1F | REG_R; |
} |
if (int32_t(imm) == -1) { // no immediate operand. push only one register |
imm = reg2 & 0x1F; |
} |
if ((imm & 0x1F) < uint32_t(reg2 & 0x1F)) { |
errors.reportLine(ERR_OPERANDS_WRONG_ORDER); |
return; |
} |
if (!(reg1 & REG_R)) { |
errors.reportLine(ERR_WRONG_OPERANDS); |
return; |
} |
if ((reg2 & REG_V) && (imm & 0x80)) { |
errors.reportLine(ERR_WRONG_OPERANDS); |
return; |
} |
code.instruction = II_POP; |
code.dest = reg1; |
code.reg1 = reg2; |
code.value.u = imm; |
code.etype = XPR_INT | XPR_REG | XPR_REG1; |
code.dtype = TYP_INT8 | (ot & 0xF); |
checkCode1(code); |
fitCode(code); |
if (lineError) return; |
codeBuffer.push(code); // save code |
} |