vc3/asm/eval.c
2025-04-02 18:39:19 +02:00

308 lines
10 KiB
C

#include "eval.h"
#include "parse.h"
#include "report.h"
#include "resolve.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static inline uint16_t eval_poperandty_unary(POperandTy ty, uint16_t operand)
{
switch (ty) {
case PoTy_Not:
return ~operand;
case PoTy_Negate:
return (uint16_t)-(int16_t)operand;
default:
return 0;
}
}
static inline uint16_t eval_poperandty_binary(
POperandTy ty, uint16_t left, uint16_t right)
{
switch (ty) {
case PoTy_Or:
return left | right;
case PoTy_Xor:
return left ^ right;
case PoTy_And:
return left & right;
case PoTy_Shl:
return (uint16_t)(left << right);
case PoTy_Shr:
return (uint16_t)(left >> right);
case PoTy_Add:
return (uint16_t)((int16_t)left + (int16_t)right);
case PoTy_Sub:
return (uint16_t)((int16_t)left - (int16_t)right);
case PoTy_Mul:
return (uint16_t)((int16_t)left * (int16_t)right);
case PoTy_Div:
return (uint16_t)((int16_t)left / (int16_t)right);
case PoTy_Mod:
return (uint16_t)((int16_t)left % (int16_t)right);
default:
return 0;
}
}
EvaledOperand eval_operand_to_imm(
OperandEvaluator* evaluator, POperand* operand)
{
switch (operand->ty) {
case PoTy_Str:
REPORTF_ERROR("%s", "strings cannot be part of expressions");
reporter_print_loc(evaluator->rep, operand->loc);
return (EvaledOperand) { .ty = EoTy_Err };
case PoTy_Mem8:
case PoTy_Mem16:
REPORTF_ERROR("%s", "indirections cannot be part of expressions");
reporter_print_loc(evaluator->rep, operand->loc);
return (EvaledOperand) { .ty = EoTy_Err };
case PoTy_Reg:
REPORTF_ERROR("%s", "registers cannot be part of expressions");
reporter_print_loc(evaluator->rep, operand->loc);
return (EvaledOperand) { .ty = EoTy_Err };
case PoTy_Imm:
return (EvaledOperand) { .ty = EoTy_Imm, .imm = operand->imm };
case PoTy_Ident:
case PoTy_SubLabel: {
const IdentResol* re
= ident_resolver_resolve(evaluator->re, operand->str);
if (re == NULL) {
if (!evaluator->second_pass) {
return (EvaledOperand) { .ty = EoTy_Imm, .imm = 0 };
}
REPORTF_ERROR("undefined identifier '%s'", operand->str);
reporter_print_loc(evaluator->rep, operand->loc);
return (EvaledOperand) { .ty = EoTy_Err };
}
switch (re->ty) {
case IdentResolTy_None:
break;
case IdentResolTy_Label:
case IdentResolTy_SubLabel:
return (EvaledOperand) { .ty = EoTy_Imm, .imm = re->ip };
case IdentResolTy_Const:
return (EvaledOperand) { .ty = EoTy_Imm, .imm = re->value };
break;
}
fprintf(stderr, "unreachable\n");
exit(1);
}
case PoTy_Not:
case PoTy_Negate: {
EvaledOperand inner
= eval_operand_to_imm(evaluator, operand->operand);
if (inner.ty == EoTy_Err) {
return inner;
} else if (inner.ty != EoTy_Imm) {
REPORTF_ERROR("%s", "operand cannot be used in expressions");
reporter_print_loc(evaluator->rep, operand->operand->loc);
return (EvaledOperand) { .ty = EoTy_Err };
}
return (EvaledOperand) {
.ty = EoTy_Imm,
.imm = eval_poperandty_unary(operand->ty, inner.imm),
};
}
case PoTy_Or:
case PoTy_Xor:
case PoTy_And:
case PoTy_Shl:
case PoTy_Shr:
case PoTy_Add:
case PoTy_Sub:
case PoTy_Mul:
case PoTy_Div:
case PoTy_Mod: {
EvaledOperand left = eval_operand_to_imm(evaluator, operand->left);
if (left.ty == EoTy_Err) {
return left;
} else if (left.ty != EoTy_Imm) {
REPORTF_ERROR("%s", "operand cannot be used in expressions");
reporter_print_loc(evaluator->rep, operand->left->loc);
return (EvaledOperand) { .ty = EoTy_Err };
}
EvaledOperand right
= eval_operand_to_imm(evaluator, operand->right);
if (right.ty == EoTy_Err) {
return right;
} else if (right.ty != EoTy_Imm) {
REPORTF_ERROR("%s", "operand cannot be used in expressions");
reporter_print_loc(evaluator->rep, operand->right->loc);
return (EvaledOperand) { .ty = EoTy_Err };
}
return (EvaledOperand) {
.ty = EoTy_Imm,
.imm = eval_poperandty_binary(operand->ty, left.imm, right.imm),
};
}
}
fprintf(stderr, "unreachable\n");
exit(1);
}
EvaledOperand eval_operand_indirection_expr(
OperandEvaluator* evaluator, POperand* operand)
{
switch (operand->ty) {
case PoTy_Reg:
return (EvaledOperand) {
.ty = EoTy_Mem8Reg,
.reg = operand->reg,
.offset = 0,
};
case PoTy_Str:
REPORTF_ERROR("%s", "strings cannot be part of indirections");
reporter_print_loc(evaluator->rep, operand->loc);
return (EvaledOperand) { .ty = EoTy_Err };
case PoTy_Mem8:
case PoTy_Mem16:
REPORTF_ERROR("%s", "indirections cannot be part of indirections");
reporter_print_loc(evaluator->rep, operand->loc);
return (EvaledOperand) { .ty = EoTy_Err };
case PoTy_Imm:
case PoTy_Ident:
case PoTy_SubLabel:
case PoTy_Not:
case PoTy_Negate:
case PoTy_Or:
case PoTy_Xor:
case PoTy_And:
case PoTy_Shl:
case PoTy_Shr:
case PoTy_Mul:
case PoTy_Div:
case PoTy_Mod: {
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
if (evaled.ty == EoTy_Err) {
return evaled;
}
return (EvaledOperand) {
.ty = EoTy_Mem8Imm,
.imm = evaled.imm,
};
}
case PoTy_Add: {
if (operand->left->ty == PoTy_Reg) {
EvaledOperand right
= eval_operand_to_imm(evaluator, operand->right);
if (right.ty == EoTy_Err) {
return right;
}
return (EvaledOperand) {
.ty = EoTy_Mem8Imm,
.reg = operand->left->reg,
.offset = right.imm,
};
} else if (operand->right->ty == PoTy_Reg) {
EvaledOperand left
= eval_operand_to_imm(evaluator, operand->left);
if (left.ty == EoTy_Err) {
return left;
}
return (EvaledOperand) {
.ty = EoTy_Mem8Imm,
.reg = operand->right->reg,
.offset = left.imm,
};
} else {
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
if (evaled.ty == EoTy_Err) {
return evaled;
}
return (EvaledOperand) {
.ty = EoTy_Mem8Imm,
.imm = evaled.imm,
};
}
break;
}
case PoTy_Sub: {
if (operand->left->ty == PoTy_Reg) {
EvaledOperand right
= eval_operand_to_imm(evaluator, operand->right);
if (right.ty == EoTy_Err) {
return right;
}
return (EvaledOperand) {
.ty = EoTy_Mem8Imm,
.reg = operand->left->reg,
.offset = (uint16_t)-(int16_t)right.imm,
};
} else if (operand->right->ty == PoTy_Reg) {
EvaledOperand left
= eval_operand_to_imm(evaluator, operand->left);
if (left.ty == EoTy_Err) {
return left;
}
return (EvaledOperand) {
.ty = EoTy_Mem8Imm,
.reg = operand->right->reg,
.offset = (uint16_t)-(int16_t)left.imm,
};
} else {
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
if (evaled.ty == EoTy_Err) {
return evaled;
}
return (EvaledOperand) {
.ty = EoTy_Mem8Imm,
.imm = evaled.imm,
};
}
break;
}
}
fprintf(stderr, "unreachable\n");
exit(1);
}
EvaledOperand eval_operand(OperandEvaluator* evaluator, POperand* operand)
{
switch (operand->ty) {
case PoTy_Str:
return (EvaledOperand) { .ty = EoTy_Str };
case PoTy_Mem8:
return eval_operand_indirection_expr(evaluator, operand->operand);
case PoTy_Mem16: {
EvaledOperand evaled
= eval_operand_indirection_expr(evaluator, operand->operand);
switch (evaled.ty) {
case EoTy_Mem8Reg:
evaled.ty = EoTy_MemU16Reg;
break;
case EoTy_Mem8Imm:
evaled.ty = EoTy_MemU16Imm;
break;
default:
break;
}
return evaled;
}
case PoTy_Reg:
return (EvaledOperand) { .ty = EoTy_Reg, .reg = operand->reg };
case PoTy_Imm:
case PoTy_Ident:
case PoTy_SubLabel:
case PoTy_Not:
case PoTy_Negate:
case PoTy_Or:
case PoTy_Xor:
case PoTy_And:
case PoTy_Shl:
case PoTy_Shr:
case PoTy_Add:
case PoTy_Sub:
case PoTy_Mul:
case PoTy_Div:
case PoTy_Mod:
return eval_operand_to_imm(evaluator, operand);
}
fprintf(stderr, "unreachable\n");
exit(1);
}