// this function could be inline void syn_memcpyw( struct machine_ops* mop, struct emitter* e, struct machine* m, operand d, operand s, operand size ){ operand iter = OP_TARGETREG( acquire_temp( mop, e, m ) ); operand src = OP_TARGETREG( acquire_temp( mop, e, m ) ); operand dst = OP_TARGETREG( acquire_temp( mop, e, m ) ); // init iterator mop->move( e, m, iter, OP_TARGETIMMED( 0 ) ); mop->move( e, m, src, s ); mop->move( e, m, dst, d ); // start loop e->ops->label_local( e, 0 ); mop->beq( e, m, iter, size, LBL_NEXT( 0 ) ); // copy mop->move( e, m, OP_TARGETDADDR( dst.reg, 0 ), OP_TARGETDADDR( src.reg, 0 ) ); // update pointers mop->add( e, m, dst, dst, OP_TARGETIMMED( -4 ) ); // TODO: this is incorrect in general but correct for copyargs mop->add( e, m, src, src, OP_TARGETIMMED( -4 ) ); // update iterator mop->add( e, m, iter, iter, OP_TARGETIMMED( 1 ) ); mop->b( e, m, LBL_PREV( 0 ) ); e->ops->label_local( e, 0 ); release_tempn( mop, e, m, 3 ); }
void syn_memsetw( struct machine_ops* mop, struct emitter* e, struct machine* m, operand d, operand v, operand size ){ operand iter = OP_TARGETREG( acquire_temp( mop, e, m ) ); operand dst = OP_TARGETREG( acquire_temp( mop, e, m ) ); // init iterator mop->move( e, m, iter, OP_TARGETIMMED( 0 ) ); mop->move( e, m, dst, d ); // start loop e->ops->label_local( e, 0 ); mop->beq( e, m, iter, size, LBL_NEXT( 0 ) ); // copy mop->move( e, m, OP_TARGETDADDR( dst.reg, 0 ), v ); // update pointers mop->add( e, m, dst, dst, OP_TARGETIMMED( 4 ) ); // update iterator mop->add( e, m, iter, iter, OP_TARGETIMMED( 1 ) ); mop->b( e, m, LBL_PREV( 0 ) ); e->ops->label_local( e, 0 ); release_tempn( mop, e, m, 2 ); }
static operand pslot_to_operand( struct frame* f, int nr_locals, int pidx, bool stackonly ){ assert( f ); assert( f->m ); if( stackonly || pidx >= NR_SPARE_REGS( f ) ){ #if 0 operand r = OP_TARGETDADDR( f->m->sp, 4 * INVERSE( pidx, 2 * nr_locals ) ); #else operand r = OP_TARGETDADDR( f->m->fp, -(8 + 4 * pidx) ); #endif return r; } else { operand r = OP_TARGETREG( vreg_to_physical_reg( f, pidx ) ); return r; } }
static void do_niling( struct machine_ops* mop, struct emitter* e, struct machine* m, operand iter, operand limit, operand dst, operand src ){ // start loop e->ops->label_local( e, 0 ); mop->beq( e, m, iter, limit, LBL_NEXT( 0 ) ); // TODO: bgt is equiv to beq so swap? // copy mop->move( e, m, OP_TARGETDADDR( dst.reg, 0 ), OP_TARGETDADDR( src.reg, 0 ) ); mop->move( e, m, OP_TARGETDADDR( dst.reg, -4 ), OP_TARGETDADDR( src.reg, -4 ) ); // update pointers mop->add( e, m, dst, dst, OP_TARGETIMMED( -8 ) ); mop->add( e, m, src, src, OP_TARGETIMMED( -8 ) ); // update iterator mop->add( e, m, iter, iter, OP_TARGETIMMED( 1 ) ); mop->b( e, m, LBL_PREV( 0 ) ); e->ops->label_local( e, 0 ); }
void popn( struct machine_ops* mop, struct emitter* e, struct machine* m, int nr_operands, ... ){ va_list ap; const operand stack = OP_TARGETREG( m->sp ); va_start( ap, nr_operands ); if( nr_operands == 1 && mop->pop ){ mop->pop( e, m, va_arg( ap, operand ) ); } else { for( int i = 0; i < nr_operands; i++ ) mop->move( e, m, va_arg( ap, operand ), OP_TARGETDADDR( m->sp, 4 * i ) ); mop->add( e, m, stack, stack, OP_TARGETIMMED( 4 * nr_operands ) ); } va_end( ap ); }
operand get_frame_closure( struct frame* f ){ return OP_TARGETDADDR( f->m->fp, -4 ); }
void jinit_epi( struct JFunc* jf, struct machine_ops* mop, struct emitter* e, struct machine* m ){ // phoney frame struct frame F = { .m = m, .nr_locals = 1, .nr_params = 0 }; struct frame *f = &F; const operand sp = OP_TARGETREG( m->sp ); const operand fp = OP_TARGETREG( m->fp ); operand rargs[ RA_SIZE ]; prefer_nontemp_acquire_reg( mop, e, f->m, RA_SIZE, rargs ); // reset stack // mop->move( e, m, sp, fp ); mop->add( e, m, sp, fp, OP_TARGETIMMED( -4 ) ); if( m->is_ra ) popn( mop, e, m , 3, rargs[ RA_DST ], fp, OP_TARGETREG( m->ra ) ); else popn( mop, e, m, 2, rargs[ RA_DST ], fp ); // pop( mop, e, m, fp ); mop->ret( e, m ); prefer_nontemp_release_reg( mop, e, f->m, RA_SIZE ); } /* * Do the majority ( function independent ) part of the prologue. That is: store the frame section, * update src and dst pointers and call the memcpy. * * The function specific code needs to set the number of params, update the stack ( requires # of locals ) * and then unspill params. */ void jinit_pro( struct JFunc* jf, struct machine_ops* mop, struct emitter* e, struct machine* m ){ // phoney frame struct frame F = { .m = m, .nr_locals = 1, .nr_params = 0 }; struct frame *f = &F; const operand sp = OP_TARGETREG( f->m->sp ); const operand fp = OP_TARGETREG( f->m->fp ); const vreg_operand basestack = vreg_to_operand( f, 0, true ); // destination is first local const int maxstack = JFUNC_UNLIMITED_STACK; operand rargs[ RA_SIZE ]; prefer_nontemp_acquire_reg( mop, e, f->m, RA_SIZE, rargs ); // push old frame pointer, closure addr / result start addr, expected nr or results if( f->m->is_ra ) pushn( mop, e, f->m, 3, OP_TARGETREG( f->m->ra), fp, rargs[ RA_SRC ] ); else pushn( mop, e, f->m, 2, fp, rargs[ RA_SRC ] ); // set ebp and update stack mop->add( e, f->m, fp, sp, OP_TARGETIMMED( 4 ) ); // point to ebp so add 4 // set src ( always start after closure see Lua VM for reason ) mop->add( e, f->m, rargs[ RA_SRC ], rargs[ RA_SRC ], OP_TARGETIMMED( -8 ) ); mop->add( e, f->m, rargs[ RA_DST ], OP_TARGETREG( basestack.value.base ), OP_TARGETIMMED( basestack.value.offset ) ); /* * Call the actual function, which is the closure. On RISC this will clobber * temp hopefully this isn't a live reg or we will get exception. On CISC * there is probably indirect direct address jmp instruction ( x86 does 0 ). */ mop->b( e, f->m, LBL_ABS( OP_TARGETDADDR( rargs[ RA_SRC ].reg, 8 ) ) ); prefer_nontemp_release_reg( mop, e, f->m, RA_SIZE ); } /* * The number of results is not know before hand. Need to update stack for future * calls. */ void jinit_vresult_postcall( struct JFunc* jf, struct machine_ops* mop, struct emitter* e, struct machine* m ){ // phoney frame struct frame F = { .m = m, .nr_locals = 1, .nr_params = 0 }; struct frame *f = &F; operand rargs[ RA_SIZE ]; prefer_nontemp_acquire_reg( mop, e, f->m, RA_SIZE, rargs ); // max stack clobber const int maxstack = 3; // prior frame has buffer of pushed return addr, frame pointer and closure // consume as many results as available mop->move( e, f->m, rargs[ RA_EXPECT ], rargs[ RA_EXIST ] ); // if register based remember return address if( f->m->is_ra ) pushn( mop, e, f->m, 1, OP_TARGETREG( f->m->ra) ); // not safe cause of stack // copy args across jfunc_call( mop, e, f->m, JF_ARG_RES_CPY, 0, maxstack, 4, rargs[ RA_SRC ], rargs[ RA_DST ], rargs[ RA_EXPECT ], rargs[ RA_EXIST ] ); if( f->m->is_ra ) popn( mop, e, f->m, 1, OP_TARGETREG( f->m->ra) ); /* * this depends heavily on copy arg implementation, it assumes ptrs will point to * the top of the stack after copying i.e. the last result copied. */ mop->add( e, m, OP_TARGETREG( f->m->sp ), rargs[ RA_DST ], OP_TARGETIMMED( 0 ) ); prefer_nontemp_release_reg( mop, e, f->m, RA_SIZE ); // return mop->ret( e, m ); }