vc3/asm/eval.c
2025-04-03 01:44:31 +02:00

381 lines
13 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>
void operand_evaluator_construct(
OperandEvaluator* evaluator, IdentResolver* re, Reporter* rep)
{
const size_t used_imms_capacity = 64;
const size_t externals_capacity = 64;
*evaluator = (OperandEvaluator) {
.re = re,
.rep = rep,
.second_pass = false,
.used_imms = malloc(sizeof(uint16_t) * used_imms_capacity),
.used_imms_size = 0,
.externals = malloc(sizeof(SurrogateExternal) * externals_capacity),
.externals_size = 0,
};
}
void operand_evaluator_destroy(OperandEvaluator* evaluator)
{
free(evaluator->used_imms);
free(evaluator->externals);
}
void operand_evaluator_reset_externals(OperandEvaluator* evaluator)
{
evaluator->used_imms_size = 0;
evaluator->externals_size = 0;
}
void operand_evaluator_use_imm(OperandEvaluator* evaluator, uint16_t imm)
{
for (size_t i = 0; i < evaluator->used_imms_size; ++i)
if (evaluator->used_imms[i] == imm)
return;
evaluator->used_imms[evaluator->used_imms_size++] = imm;
}
uint16_t operand_evaluator_use_external(
OperandEvaluator* evaluator, const IdentResol* resol)
{
uint16_t surrogate;
if (evaluator->externals_size > 0 && evaluator->used_imms_size > 0) {
surrogate
= evaluator->externals[evaluator->externals_size - 1].surrogate - 1;
while (surrogate > 0) {
bool used = false;
for (size_t i = 0; i < evaluator->used_imms_size; ++i)
if (evaluator->used_imms[i] == surrogate)
used = true;
if (!used)
break;
used -= 1;
}
} else {
surrogate = 0xffff;
}
evaluator->externals[evaluator->externals_size++] = (SurrogateExternal) {
.resol = resol,
.extern_id = resol->extern_id,
.surrogate = surrogate,
};
return surrogate;
}
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 };
case IdentResolTy_Extern:
return (EvaledOperand) {
.ty = EoTy_Imm,
.imm = operand_evaluator_use_external(evaluator, re),
};
}
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: {
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
if (evaled.ty == EoTy_Imm) {
operand_evaluator_use_imm(evaluator, evaled.imm);
}
return evaled;
}
}
fprintf(stderr, "unreachable\n");
exit(1);
}