📄 code.cpp
字号:
/* CODE.CPP
* Code generation
* UnderC C++ interpreter
* Steve Donovan, 2001
* This is GPL'd software, and the usual disclaimers apply.
* See LICENCE
*/
#include "common.h"
#include "code.h"
#include "opcodes.h"
#include "tparser.h"
#include "directcall.h"
#include "loaded_module_list.h"
#include <map>
namespace {
std::map<int,int> equiv_op,float_equiv;
}
typedef unsigned char uchar;
int make_word(uchar c1, uchar c2)
{
return c1 + (c2 << 8);
}
int builtin_conversion(Type t, Type te);
void UCContext::jump(int op, Label *lbl)
{
emit(op,NONE,lbl->addr());
}
void UCContext::emit(int opcode, PExpr e)
{
if (!e) emit(opcode,NONE,0);
else {
assert(e->is_entry());
PEntry pe = e->entry();
if (pe->data == THIS_OFFSET) emit(PUSH_THIS,NONE,0);
else emit(opcode,pe->rmode,pe->data);
}
}
void UCContext::emit(int opcode, int rm, int data)
{
emitc(opcode,(RMode)rm,data);
}
void UCContext::emit_data_instruction(int opcode, unsigned long data)
{
// The operand for a number of instructions is a 20bit offset to an allocated 32-bit word.
emit(opcode,DIRECT,Parser::global().alloc_int(data));
}
typedef unsigned long ulong;
void UCContext::emit_push_int(int sz)
{
emit_data_instruction(PUSHI,(ulong) sz);
}
void UCContext::emit_type_instr(int opcode, Type t)
{
emit_data_instruction(opcode,Parser::AsTType(t));
}
void UCContext::emit_except_throw(Type t)
{
emit_type_instr(THROW_EX,t);
}
void UCContext::emit_dynamic_cast(Type t)
{
emit_type_instr(DCAST,t);
}
void UCContext::init()
{
static int equivalents[] = {STAR,MUL,DIVIDE,DIV,PLUS,ADD,MINUS,SUB,
MODULO,MOD,LSHIFT,SHL,RSHIFT,SHR,BIN_AND,AND,BIN_OR,OR,
BIN_XOR,XOR,EQUAL,EQ,NOT_EQUAL,NEQ,LESS_THAN,LESS,GREATER,GREAT,
LEQ,LE,GEQ,GE,UMINUS,NEG,LOG_NOT,NOT};
static int float_equivalents[] = {MUL,FMUL,DIV,FDIV,ADD,FADD,SUB,FSUB,
EQ,FEQ,LESS,FLESS,GREAT,FGREAT,NEG,FNEG,NEQ,FNEQ,LE,FLE,GE,FGE};
int i;
for (i = 0; i < sizeof(equivalents)/sizeof(int); i+=2)
equiv_op[equivalents[i]] = equivalents[i+1];
for (i = 0; i < sizeof(float_equivalents)/sizeof(int); i+=2)
float_equiv[float_equivalents[i]] = float_equivalents[i+1];
}
// *fix 0.9.3 Replaced naive is_double check in builtin conversions and stack droppings
bool is_double_number(const Type& t)
{ return t.is_double() && !t.is_pointer() /*!t.is_ref_or_ptr()*/ ; }
int UCContext::builtin_conversion(Type t, Type te)
{
// *fix 1.1.4 We could not do a simple float to int conversion!
// note: returning 0 means no operation!
if (t.is_int()) {
if(te.is_int()) return 0;
if(!te.is_pointer()) {
if(te.is_double()) return(D2I); else
if(te.is_float()) return(F2I);
else return 0;
}
else return 0;
} else
if (t.is_float()) {
if(t.is_double() && !t.is_pointer()) {
if(te.is_int()) return(I2D); else
if(te.is_single()) return(F2D); else return 0;
} else
if(t.is_single()) {
if(te.is_double() && !te.is_pointer()) return(D2F); else
if(te.is_int()) return(I2F); else return 0;
}
else return 0;
}
else return 0;
}
///------------------Label class and management functions-----------------------------
void Label::here(int offs)
{
m_offset = m_code->ip_offset()+offs;
if (m_refs.size() > 0)
for(LI p = m_refs.begin(); p != m_refs.end(); ++p)
(*p)->data = m_offset;
}
int Label::addr()
{
if(m_offset >= 0) return m_offset;
m_refs.push_back(m_code->current_pi());
return 0;
}
void Label::remove(Instruction *pi)
{
m_refs.remove(pi);
}
// *add 1.2.5 A specialized operation: patch the instruction before the first reference with a NOP
void Label::patch_with_NOP()
{
Instruction* ii = m_refs.front();
ii--;
ii->opcode = HALT;
ii->rmode = 0;
ii->data = 999;
}
///---------- Switch block generation
SwitchBlock::SwitchBlock(UCContext *code) : m_code(code), m_default_jmp(0)
{
m_start = code->current_pi();
}
void SwitchBlock::add_jump(int val)
{
m_list.push_back(val);
m_list.push_back(m_code->ip_offset());
}
void SwitchBlock::add_default()
{
m_default_jmp = m_code->ip_offset();
}
SwitchBlock *SwitchBlock::construct()
{
if(!m_default_jmp) m_default_jmp = m_code->ip_offset();
int sz = m_list.size()+2;
int *block = new int[sz];
block[0] = m_list.size()/2;
block[1] = m_default_jmp;
IntList::iterator ili;
int i = 2;
for(ili = m_list.begin(); ili != m_list.end(); ++ili)
block[i++] = *ili;
m_start->data = Parser::global().alloc(sz*sizeof(int),block);
delete block;
return this;
}
void UCContext::emit_stack_op(int op, Type t)
{
// the stack operations DUP & DROP come in single and double varieties.
if (is_double_number(t)) emit(op+1);
else if (t != t_void) emit(op);
}
// a var encodes in three distinct ways
// a NULL reference means that this is relative to TOS!
void UCContext::emit_reference(PExpr ex, int flags, int tcode)
{
if (ex) {
PEntry pe = ex->entry();
Type t = ex->type();
// *add 1.1.4 bit fields have their own instruction
if (pe->is_bitfield()) {
emit ((flags & LVALUE ? POPIF : PUSHIF),ex);
} else
// special encoding for _function_ IREFs. Clearly this is not
// complete - it assumes that there is only one fn in the set!
if (t.is_function()) {
FunctionEntry *pfe = (FunctionEntry *)pe->data;
emit_push_int((int) pfe->back()->fun_block());
} else
// *fix 1.2.0 Special case for regular references; their 'value' is a pointer
if ((flags & AS_PTR) && ! t.is_plain_reference()) emit(PEA,ex);
else emit ( (flags & LVALUE ? POPC : PUSHC) + tcode,ex);
} else {
if (!(flags & AS_PTR)) emit( (flags & LVALUE ? POPSC : PUSHSC)+tcode,NONE,0);
}
}
#include <stdarg.h>
int choose(int val,...)
{
int key,value;
va_list ap;
va_start(ap,val);
do {
key = va_arg(ap,int);
if (key==0) return 0; // not found!
value = va_arg(ap,int);
} while (val != key);
va_end(ap);
return value;
}
enum { CHAR_SZ, SHORT_SZ, POINTER_SZ, DOUBLE_SZ };
int size_code(Type t)
{
if (t.is_pointer()) return POINTER_SZ;
if (t.is_char() || t.is_bool()) return CHAR_SZ; // *fix 1.2.4 bools are now one byte
if (t.is_short()) return SHORT_SZ;
if (t.is_double()) return DOUBLE_SZ;
else return POINTER_SZ;
}
bool passthrough_conversion(Type t,PExpr ex)
{
if (ex==NULL) return false;
if (ex->op() != BCAST) return false;
return UCContext::builtin_conversion(t,ex->type()) == 0;
}
// interpretation of flags....
// bool dynamic() { return flags & 2; }
// bool load_ODS() { return flags & 1; }
int ConstructBlock::make(PExpr er, int sz, bool is_dynamic, FBlock *pfb)
{
using Parser::global;
int icb = global().alloc(sizeof(ConstructBlock),NULL);
ConstructBlock *pcb = (ConstructBlock *)global().addr(icb);
Class *pc = pfb->class_ptr;
pcb->cls = pc;
pcb->vmt = pc->get_VMT();
pcb->no = sz;
pcb->pfb = pfb;
pcb->sz = pc->size();
pcb->flags = 0;
if (is_dynamic) pcb->flags = 2;
if (er != NULL && er->is_entry()) { //*NOTE*
bool is_auto = er->entry()->is_stack_relative();
pcb->flags += 1;
if (! is_auto) pcb->flags += 4;
}
return icb;
}
void push_object_ptr(UCContext *cntxt, PExpr e)
{
// push expression on the Object Stack
// if (Parser::state.in_method()) cntxt->pushed_op(true);
if (e && e->is_entry() && e->entry()->type.is_object()) cntxt->emit(LOS,e);
else {
if (e) cntxt->compile(e);
cntxt->emit(LOSS);
}
}
int offset_to_fun_block(Function *pf)
{
return Parser::global().offset(pf->fun_block());
}
static bool dont_use_map(Function *pf)
{
return pf->class_context()->has_true_VMT();
}
void compile_function_call(UCContext* code, int op, int flags, PExpr ex, bool use_obj=false, PExpr obj=NULL, int icb=0, bool is_dynamic=false)
{
// *fix 0.9.6 The DCALL function type _forces_ a function to be directly called, virtual or not
PExprList args = ex->arg_list();
Type ret_type;
Instruction* pi;
bool function_ptr_call = op==EXPR || op==EXPR_METHOD;
PFunction pf = function_ptr_call ? NULL : ex->function();
int sz = 0;
// If this was a method, then the object ptr is at the end of the arguments
//*PASOP* Also true for _static methods_???
//*unless we are explicitly given an object ptr, of course.
if (!use_obj && (op==METHOD_CALL || op==EXPR_METHOD)) {
obj = args->back();
args->pop_back();
}
// compile the args in _reverse_ order!
ExprList::reverse_iterator ali;
if (args != NULL)
for (ali = args->rbegin(); ali != args->rend(); ++ali) {
code->compile(*ali);
if (is_double_number((*ali)->type())) {
sz += 2;
} else ++sz;
}
bool was_method_call = obj != NULL || is_dynamic;
if (was_method_call) {// the object stack is pushed immediately before the call
push_object_ptr(code,obj); // obj ptr onto the object stack
}
// *change 1.2.0 PUSH_THIS etc has been moved to emit_native_function_call()
if (function_ptr_call) { // call via a function ptr...
PExpr e1 = ex->arg1();
ret_type = e1->type().as_signature()->return_type();
code->compile(e1);
// issue: stdarg??
code->emit(CALLS);
// and obviously we gotta do VCALLS....
} else {
ret_type = pf->return_type();
// calls to native cdecl-style routines like printf()
// require us to push the number of arguments!
if (pf->stdarg() && pf->builtin()) code->emit_push_int(sz);
// *add 1.2.3b Single-instruction functions can be safely inlined
if (Parser::debug.attempt_inline && (pi = CodeGenerator::has_one_instruction(pf)) != NULL) {
code->out(pi);
} else {
// *add 1.2.0 Imported classes may need the VMT-map equivalents!
// *add 1.2.4 Virtual calls are proceeded by a 'NOP' carrying the original fun block!
if (pf->is_virtual() && op!=DCALL) {
if (! Parser::debug.attempt_inline) code->emit(HALT,DIRECT,offset_to_fun_block(pf));
code->emit(dont_use_map(pf) ? VCALL : VCALLX,DIRECT,pf->slot_id());
} else if (icb != 0) {
if (icb > 0)
code->emit(CCALLV,DIRECT,icb); // vector ctor/dor,
else
code->emit(dont_use_map(pf) ? CCALL : CCALLX ,DIRECT,offset_to_fun_block(pf)); // scalar ctor w/ VMT.
} else { // plain call, no frills.
code->emit(CALL,DIRECT,offset_to_fun_block(pf));
}
// *add 1.2.3b Correct stack pointer for cdecl calls (remember that builtins get arg sz pushed as well!)
int sp_diff,rt;
if (pf->stdarg()
&& (sp_diff = sz - pf->fun_block()->nargs + (pf->builtin() ? 1 : 0)) > 0)
{
if (is_double_number(ret_type)) rt = 2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -