mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-26 19:23:41 +02:00

useless i64 constants on the stack with random numbers and then drop them. It was used for primitive debugging, since previously it wasn't possible to identify the function name. Now we can produce DWARF debug info, as well as a "names" custom section to give each function a name and most WebAssembly environments support at least one of the two mechanisms. Therefore the g_fingerprint hack is no longer necessary.
2831 lines
113 KiB
ObjectPascal
2831 lines
113 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2010 by Florian Klaempfl and Jonas Maebe
|
|
Member of the Free Pascal development team
|
|
|
|
This unit implements the WebAssembly high level code generator
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
****************************************************************************
|
|
}
|
|
unit hlcgcpu;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
globtype,
|
|
aasmbase,aasmdata,aasmcpu,
|
|
symbase,symconst,symtype,symdef,symsym,
|
|
node,
|
|
cpubase, hlcgobj, cgbase, cgutils, parabase, wasmdef;
|
|
|
|
type
|
|
|
|
{ thlcgwasm }
|
|
|
|
thlcgwasm = class(thlcgobj)
|
|
private
|
|
fevalstackheight,
|
|
fmaxevalstackheight: longint;
|
|
|
|
{ checks whether the type needs special methodptr-like handling, when stored
|
|
in a LOC_REGISTER location. This applies to the following types:
|
|
- method pointers
|
|
- 8-byte records
|
|
- nested proc ptrs
|
|
When stored in a LOC_REGISTER tlocation, these types use both register
|
|
and registerhi with the following sizes:
|
|
|
|
register - cgsize = int_cgsize(voidcodepointertype.size)
|
|
registerhi - cgsize = int_cgsize(voidpointertype.size) or int_cgsize(parentfpvoidpointertype.size)
|
|
(check d.size to determine which one of the two)
|
|
}
|
|
function is_methodptr_like_type(d:tdef): boolean;
|
|
public
|
|
fntypelookup : TWasmProcTypeLookup;
|
|
|
|
constructor create;
|
|
destructor Destroy; override;
|
|
|
|
function RefStackPointerSym: TWasmGlobalAsmSymbol;
|
|
|
|
procedure incstack(list : TAsmList;slots: longint);
|
|
procedure decstack(list : TAsmList;slots: longint);
|
|
|
|
class function def2regtyp(def: tdef): tregistertype; override;
|
|
function getintregister(list:TAsmList;size:tdef):Tregister;override;
|
|
function getregisterfordef(list: TAsmList;size:tdef):Tregister;override;
|
|
|
|
procedure a_load_const_cgpara(list : TAsmList;tosize : tdef;a : tcgint;const cgpara : TCGPara);override;
|
|
|
|
function a_call_name(list : TAsmList;pd : tprocdef;const s : TSymStr; const paras: array of pcgpara; forceresdef: tdef; weak: boolean): tcgpara;override;
|
|
function a_call_reg(list: TAsmList; pd: tabstractprocdef; reg: tregister; const paras: array of pcgpara): tcgpara; override;
|
|
|
|
{ move instructions - a_load_FROM_TO }
|
|
procedure a_load_const_reg(list : TAsmList;tosize : tdef;a : tcgint;register : tregister);override;
|
|
procedure a_load_const_ref(list : TAsmList;tosize : tdef;a : tcgint;const ref : treference);override;
|
|
procedure a_load_reg_ref(list : TAsmList;fromsize, tosize : tdef;register : tregister;const ref : treference);override;
|
|
procedure a_load_reg_reg(list : TAsmList;fromsize, tosize : tdef;reg1,reg2 : tregister);override;
|
|
procedure a_load_ref_reg(list : TAsmList;fromsize, tosize : tdef;const ref : treference;register : tregister);override;
|
|
procedure a_load_ref_ref(list : TAsmList;fromsize, tosize : tdef;const sref : treference;const dref : treference);override;
|
|
procedure a_load_loc_ref(list : TAsmList;fromsize, tosize: tdef; const loc: tlocation; const ref : treference);override;
|
|
procedure a_loadaddr_ref_reg(list : TAsmList;fromsize, tosize : tdef;const ref : treference;r : tregister);override;
|
|
procedure a_load_subsetref_regs_index(list: TAsmList; subsetsize: tdef; loadbitsize: byte; const sref: tsubsetreference; valuereg: tregister); override;
|
|
procedure a_load_regconst_subsetref_intern(list : TAsmList; fromsize, subsetsize: tdef; fromreg: tregister; const sref: tsubsetreference; slopt: tsubsetloadopt); override;
|
|
|
|
{ basic arithmetic operations }
|
|
procedure a_op_const_reg(list: TAsmList; Op: TOpCG; size: tdef; a: tcgint; reg: TRegister); override;
|
|
procedure a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tdef; a: tcgint; src, dst: tregister); override;
|
|
procedure a_op_const_ref(list: TAsmList; Op: TOpCG; size: tdef; a: tcgint; const ref: TReference); override;
|
|
|
|
procedure a_op_ref_reg(list: TAsmList; Op: TOpCG; size: tdef; const ref: TReference; reg: TRegister); override;
|
|
procedure a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tdef; src1, src2, dst: tregister); override;
|
|
procedure a_op_reg_reg(list: TAsmList; Op: TOpCG; size: tdef; reg1, reg2: TRegister); override;
|
|
procedure a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; a: tcgint; src, dst: tregister;setflags : boolean;var ovloc : tlocation); override;
|
|
procedure a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; src1, src2, dst: tregister;setflags : boolean;var ovloc : tlocation); override;
|
|
|
|
procedure a_cmp_const_ref_label(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; const ref: treference; l: tasmlabel); override;
|
|
procedure a_cmp_const_reg_label(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; reg: tregister; l: tasmlabel); override;
|
|
procedure a_cmp_ref_reg_label(list: TAsmList; size: tdef; cmp_op: topcmp; const ref: treference; reg: tregister; l: tasmlabel); override;
|
|
procedure a_cmp_reg_ref_label(list: TAsmList; size: tdef; cmp_op: topcmp; reg: tregister; const ref: treference; l: tasmlabel); override;
|
|
procedure a_cmp_reg_reg_label(list: TAsmList; size: tdef; cmp_op: topcmp; reg1, reg2: tregister; l: tasmlabel); override;
|
|
|
|
procedure a_jmp_always(list : TAsmList;l: tasmlabel); override;
|
|
procedure a_jmp_always_pascal_goto(list : TAsmList;l: tasmlabel); override;
|
|
|
|
procedure a_loadfpu_ref_ref(list: TAsmList; fromsize, tosize: tdef; const ref1, ref2: treference); override;
|
|
procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; reg: tregister); override;
|
|
procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tdef; reg: tregister; const ref: treference); override;
|
|
procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister); override;
|
|
|
|
procedure g_unreachable(list: TAsmList); override;
|
|
|
|
procedure g_concatcopy(list : TAsmList;size: tdef; const source,dest : treference); override;
|
|
|
|
procedure g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean); override;
|
|
procedure g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean); override;
|
|
|
|
procedure g_rangecheck(list: TAsmList; const l:tlocation; fromdef,todef: tdef); override;
|
|
|
|
procedure g_overflowcheck(list: TAsmList; const Loc: tlocation; def: tdef); override;
|
|
procedure g_overflowCheck_loc(List:TAsmList;const Loc:TLocation;def:TDef;var ovloc : tlocation); override;
|
|
|
|
procedure maybe_change_load_node_reg(list: TAsmList; var n: tnode; reload: boolean); override;
|
|
|
|
procedure gen_entry_code(list: TAsmList); override;
|
|
procedure gen_exit_code(list: TAsmList); override;
|
|
|
|
procedure gen_stack_check_size_para(list: TAsmList); override;
|
|
procedure gen_stack_check_call(list: TAsmList); override;
|
|
|
|
{ unimplemented/unnecessary routines }
|
|
procedure a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tdef; src, dst: tregister); override;
|
|
procedure a_loadmm_loc_reg(list: TAsmList; fromsize, tosize: tdef; const loc: tlocation; const reg: tregister; shuffle: pmmshuffle); override;
|
|
procedure a_loadmm_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister; shuffle: pmmshuffle); override;
|
|
procedure a_loadmm_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; reg: tregister; shuffle: pmmshuffle); override;
|
|
procedure a_loadmm_reg_ref(list: TAsmList; fromsize, tosize: tdef; reg: tregister; const ref: treference; shuffle: pmmshuffle); override;
|
|
procedure a_opmm_reg_reg(list: TAsmList; Op: TOpCG; size: tdef; src, dst: tregister; shuffle: pmmshuffle); override;
|
|
procedure a_loadmm_intreg_reg(list: TAsmList; fromsize, tosize: tdef; intreg, mmreg: tregister; shuffle: pmmshuffle); override;
|
|
procedure a_loadmm_reg_intreg(list: TAsmList; fromsize, tosize: tdef; mmreg, intreg: tregister; shuffle: pmmshuffle); override;
|
|
procedure g_stackpointer_alloc(list: TAsmList; size: longint); override;
|
|
procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint); override;
|
|
procedure g_adjust_self_value(list: TAsmList; procdef: tprocdef; ioffset: aint); override;
|
|
procedure g_local_unwind(list: TAsmList; l: TAsmLabel); override;
|
|
|
|
{ Wasm-specific routines }
|
|
|
|
procedure g_procdef(list:TAsmList;pd: tprocdef;is_forward: Boolean);
|
|
procedure g_maybe_checkforexceptions(list:TasmList); override;
|
|
procedure g_load_check_simple(list: TAsmList; const ref: treference; size: aint);
|
|
|
|
procedure a_load_stack_reg(list : TAsmList;size: tdef;reg: tregister);
|
|
{ extra_slots are the slots that are used by the reference, and that
|
|
will be removed by the store operation }
|
|
procedure a_load_stack_ref(list : TAsmList;size: tdef;const ref: treference;extra_slots: longint);
|
|
procedure a_load_reg_stack(list : TAsmList;size: tdef;reg: tregister);
|
|
{ extra_slots are the slots that are used by the reference, and that
|
|
will be removed by the load operation }
|
|
procedure a_load_ref_stack(list : TAsmList;size: tdef;const ref: treference;extra_slots: longint);
|
|
procedure a_load_const_stack(list : TAsmList;size: tdef;a :tcgint; typ: TRegisterType);
|
|
procedure a_load_subsetref_stack(list : TAsmList;size: tdef; const sref: tsubsetreference);
|
|
procedure a_loadaddr_ref_stack(list : TAsmList;fromsize, tosize : tdef;const ref : treference);
|
|
|
|
procedure a_load_stack_loc(list : TAsmList;size: tdef;const loc: tlocation);
|
|
procedure a_load_loc_stack(list : TAsmList;size: tdef;const loc: tlocation);
|
|
|
|
procedure a_loadfpu_const_stack(list : TAsmList;size: tdef;a :double);
|
|
|
|
procedure a_op_stack(list : TAsmList;op: topcg; size: tdef);
|
|
procedure a_op_const_stack(list : TAsmList;op: topcg; size: tdef;a : tcgint);
|
|
procedure a_op_reg_stack(list : TAsmList;op: topcg; size: tdef;reg: tregister);
|
|
procedure a_op_ref_stack(list : TAsmList;op: topcg; size: tdef;const ref: treference);
|
|
procedure a_op_loc_stack(list : TAsmList;op: topcg; size: tdef;const loc: tlocation);
|
|
|
|
procedure a_cmp_const_loc_stack(list: TAsmList; size: tdef;cmp_op: topcmp; a: tcgint; const loc: tlocation);
|
|
procedure a_cmp_const_ref_stack(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; const ref: treference);
|
|
procedure a_cmp_const_reg_stack(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; reg: tregister);
|
|
procedure a_cmp_ref_reg_stack(list: TAsmList; size: tdef; cmp_op: topcmp; const ref: treference; reg: tregister);
|
|
procedure a_cmp_reg_ref_stack(list: TAsmList; size: tdef; cmp_op: topcmp; reg: tregister; const ref: treference);
|
|
procedure a_cmp_reg_reg_stack(list: TAsmList; size: tdef; cmp_op: topcmp; reg1, reg2: tregister);
|
|
procedure a_cmp_subsetreg_reg_stack(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sreg: tsubsetregister; reg: tregister);
|
|
procedure a_cmp_subsetref_reg_stack(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sref: tsubsetreference; reg: tregister);
|
|
|
|
procedure a_cmp_loc_reg_stack(list : TAsmList;size : tdef;cmp_op : topcmp; const loc: tlocation; reg : tregister);
|
|
procedure a_cmp_reg_loc_stack(list : TAsmList;size : tdef;cmp_op : topcmp; reg: tregister; const loc: tlocation);
|
|
procedure a_cmp_ref_loc_stack(list: TAsmList; size: tdef;cmp_op: topcmp; const ref: treference; const loc: tlocation);
|
|
|
|
procedure a_cmp_const_loc_br(list: TAsmList; size: tdef;cmp_op: topcmp; a: tcgint; const loc: tlocation; br: Integer);
|
|
procedure a_cmp_const_ref_br(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; const ref: treference; br: Integer);
|
|
procedure a_cmp_const_reg_br(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; reg: tregister; br: Integer);
|
|
procedure a_cmp_ref_reg_br(list: TAsmList; size: tdef; cmp_op: topcmp; const ref: treference; reg: tregister; br: Integer);
|
|
procedure a_cmp_reg_ref_br(list: TAsmList; size: tdef; cmp_op: topcmp; reg: tregister; const ref: treference; br: Integer);
|
|
procedure a_cmp_reg_reg_br(list: TAsmList; size: tdef; cmp_op: topcmp; reg1, reg2: tregister; br: Integer);
|
|
procedure a_cmp_subsetreg_reg_br(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sreg: tsubsetregister; reg: tregister; br: Integer);
|
|
procedure a_cmp_subsetref_reg_br(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sref: tsubsetreference; reg: tregister; br: Integer);
|
|
|
|
procedure a_cmp_loc_reg_br(list : TAsmList;size : tdef;cmp_op : topcmp; const loc: tlocation; reg : tregister; br: Integer);
|
|
procedure a_cmp_reg_loc_br(list : TAsmList;size : tdef;cmp_op : topcmp; reg: tregister; const loc: tlocation; br: Integer);
|
|
procedure a_cmp_ref_loc_br(list: TAsmList; size: tdef;cmp_op: topcmp; const ref: treference; const loc: tlocation; br: Integer);
|
|
|
|
procedure g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation); override;
|
|
|
|
procedure a_cmp_stack_stack(list : TAsmlist; size: tdef; cmp_op: topcmp);
|
|
{ truncate/sign extend after performing operations on values < 32 bit
|
|
that may have overflowed outside the range }
|
|
procedure maybe_adjust_op_result(list: TAsmList; op: TOpCg; size: tdef);
|
|
|
|
{ performs sign/zero extension as required }
|
|
procedure resize_stack_int_val(list: TAsmList;fromsize,tosize: tdef; formemstore: boolean);
|
|
|
|
{ 8/16 bit unsigned parameters and return values must be sign-extended on
|
|
the producer side, because the JVM does not support unsigned variants;
|
|
then they have to be zero-extended again on the consumer side }
|
|
procedure maybe_resize_stack_para_val(list: TAsmList; retdef: tdef; callside: boolean);
|
|
|
|
{ adjust the stack height after a call based on the specified number of
|
|
slots used for parameters and the provided resultdef }
|
|
procedure g_adjust_stack_after_call(list: TAsmList; pd: tabstractprocdef);
|
|
|
|
property maxevalstackheight: longint read fmaxevalstackheight;
|
|
|
|
protected
|
|
procedure gen_load_uninitialized_function_result(list: TAsmList; pd: tprocdef; resdef: tdef; const resloc: tcgpara); override;
|
|
|
|
function g_call_system_proc_intern(list: TAsmList; pd: tprocdef; const paras: array of pcgpara; forceresdef: tdef): tcgpara; override;
|
|
|
|
public
|
|
{ in case of an array, the array base address and index have to be
|
|
put on the evaluation stack before the stored value; similarly, for
|
|
fields the self pointer has to be loaded first. Also checks whether
|
|
the reference is valid. If dup is true, the necessary values are stored
|
|
twice. Returns how many stack slots have been consumed, disregarding
|
|
the "dup". }
|
|
function prepare_stack_for_ref(list: TAsmList; var ref: treference; dup: boolean): longint;
|
|
|
|
procedure gen_load_cgpara_loc(list: TAsmList; vardef: tdef; const para: TCGPara; var destloc: tlocation; reusepara: boolean);override;
|
|
protected
|
|
{ return the load/store opcode to load/store from/to ref; if the result
|
|
has to be and'ed after a load to get the final value, that constant
|
|
is returned in finishandval (otherwise that value is set to -1) }
|
|
function loadstoreopcref(def: tdef; isload: boolean; const ref: treference; out finishandval: tcgint): tasmop;
|
|
procedure resizestackfpuval(list: TAsmList; fromsize, tosize: tcgsize);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
verbose,cutils,globals,fmodule,constexp,
|
|
defutil,cpupi,
|
|
aasmtai,
|
|
symtable,symcpu,
|
|
procinfo,cpuinfo,cgobj,cgcpu,tgobj,tgcpu,paramgr;
|
|
|
|
const
|
|
TOpCG2IAsmOp : array[topcg] of TAsmOp=(
|
|
A_None, {OP_NONE}
|
|
A_None, {OP_MOVE, replaced operation with direct load }
|
|
a_i32_add, {OP_ADD, simple addition }
|
|
a_i32_and, {OP_AND, simple logical and }
|
|
a_i32_div_u, {OP_DIV, simple unsigned division }
|
|
a_i32_div_s, {OP_IDIV, simple signed division }
|
|
a_i32_mul, {OP_IMUL, simple signed multiply }
|
|
a_i32_mul, {OP_MUL, simple unsigned multiply }
|
|
A_None, {OP_NEG, simple negate } // neg = xor + 1
|
|
A_None, {OP_NOT, simple logical not } // not = xor - 1
|
|
a_i32_or, {OP_OR, simple logical or }
|
|
a_i32_shr_s, {OP_SAR, arithmetic shift-right }
|
|
a_i32_shl, {OP_SHL, logical shift left }
|
|
a_i32_shr_u, {OP_SHR, logical shift right }
|
|
a_i32_sub, {OP_SUB, simple subtraction }
|
|
a_i32_xor, {OP_XOR, simple exclusive or }
|
|
a_i32_rotl, {OP_ROL, rotate left }
|
|
a_i32_rotr {OP_ROR rotate right }
|
|
);
|
|
TOpCG2LAsmOp : array[topcg] of TAsmOp=(
|
|
A_None, {OP_NONE}
|
|
a_i64_load, {OP_MOVE, replaced operation with direct load }
|
|
a_i64_add, {OP_ADD, simple addition }
|
|
a_i64_and, {OP_AND, simple logical and }
|
|
a_i64_div_u, {OP_DIV, simple unsigned division }
|
|
a_i64_div_s, {OP_IDIV, simple signed division }
|
|
a_i64_mul, {OP_IMUL, simple signed multiply }
|
|
a_i64_mul, {OP_MUL, simple unsigned multiply }
|
|
A_None, {OP_NEG, simple negate } // neg = xor + 1
|
|
A_None, {OP_NOT, simple logical not } // not = xor - 1
|
|
a_i64_or, {OP_OR, simple logical or }
|
|
a_i64_shr_s, {OP_SAR, arithmetic shift-right }
|
|
a_i64_shl, {OP_SHL, logical shift left }
|
|
a_i64_shr_u, {OP_SHR, logical shift right }
|
|
a_i64_sub, {OP_SUB, simple subtraction }
|
|
a_i64_xor, {OP_XOR, simple exclusive or }
|
|
a_i64_rotl, {OP_ROL, rotate left }
|
|
a_i64_rotr {OP_ROR rotate right }
|
|
);
|
|
|
|
function thlcgwasm.is_methodptr_like_type(d:tdef): boolean;
|
|
var
|
|
is_8byterecord, is_methodptr, is_nestedprocptr: Boolean;
|
|
begin
|
|
is_8byterecord:=(d.typ=recorddef) and (d.size=8);
|
|
is_methodptr:=(d.typ=procvardef)
|
|
and (po_methodpointer in tprocvardef(d).procoptions)
|
|
and not(po_addressonly in tprocvardef(d).procoptions);
|
|
is_nestedprocptr:=(d.typ=procvardef)
|
|
and is_nested_pd(tprocvardef(d))
|
|
and not(po_addressonly in tprocvardef(d).procoptions);
|
|
result:=is_8byterecord or is_methodptr or is_nestedprocptr;
|
|
end;
|
|
|
|
function thlcgwasm.RefStackPointerSym: TWasmGlobalAsmSymbol;
|
|
begin
|
|
result:=TWasmGlobalAsmSymbol(current_asmdata.RefAsmSymbolByClass(TWasmGlobalAsmSymbol,STACK_POINTER_SYM,AT_WASM_GLOBAL));
|
|
result.WasmGlobalType:=wbt_i32;
|
|
end;
|
|
|
|
constructor thlcgwasm.create;
|
|
begin
|
|
fevalstackheight:=0;
|
|
fmaxevalstackheight:=0;
|
|
fntypelookup:=TWasmProcTypeLookup.Create;
|
|
end;
|
|
|
|
destructor thlcgwasm.Destroy;
|
|
begin
|
|
fntypelookup.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure thlcgwasm.incstack(list: TAsmList; slots: longint);
|
|
begin
|
|
if (fevalstackheight<0) and
|
|
not(cs_no_regalloc in current_settings.globalswitches) then
|
|
{$ifdef DEBUG_WASMSTACK}
|
|
list.concat(tai_comment.Create(strpnew('!!! stack underflow')));
|
|
{$else DEBUG_WASMSTACK}
|
|
internalerror(2010120501);
|
|
{$endif DEBUG_WASMSTACK}
|
|
if slots=0 then
|
|
exit;
|
|
inc(fevalstackheight,slots);
|
|
if (fevalstackheight>fmaxevalstackheight) then
|
|
fmaxevalstackheight:=fevalstackheight;
|
|
if cs_asm_regalloc in current_settings.globalswitches then
|
|
list.concat(tai_comment.Create(strpnew(' allocated '+tostr(slots)+', stack height = '+tostr(fevalstackheight))));
|
|
end;
|
|
|
|
procedure thlcgwasm.decstack(list: TAsmList;slots: longint);
|
|
begin
|
|
if slots=0 then
|
|
exit;
|
|
dec(fevalstackheight,slots);
|
|
if (fevalstackheight<0) and
|
|
not(cs_no_regalloc in current_settings.globalswitches) then
|
|
{$ifdef DEBUG_WASMSTACK}
|
|
list.concat(tai_comment.Create(strpnew('!!! stack underflow')));
|
|
{$else DEBUG_WASMSTACK}
|
|
internalerror(2010120501);
|
|
{$endif DEBUG_WASMSTACK}
|
|
if cs_asm_regalloc in current_settings.globalswitches then
|
|
list.concat(tai_comment.Create(strpnew(' freed '+tostr(slots)+', stack height = '+tostr(fevalstackheight))));
|
|
end;
|
|
|
|
|
|
class function thlcgwasm.def2regtyp(def: tdef): tregistertype;
|
|
begin
|
|
if is_wasm_externref(def) then
|
|
result:=R_EXTERNREFREGISTER
|
|
else if is_wasm_funcref(def) then
|
|
result:=R_FUNCREFREGISTER
|
|
else if (def.typ=recorddef) and (def.size in [4,8]) and (trecorddef(def).contains_float_field) then
|
|
result:=R_FPUREGISTER
|
|
else
|
|
result:=inherited;
|
|
end;
|
|
|
|
|
|
function thlcgwasm.getintregister(list:TAsmList;size:tdef):Tregister;
|
|
begin
|
|
if is_wasm_reference_type(size) then
|
|
internalerror(2023060702)
|
|
else
|
|
result:=inherited;
|
|
end;
|
|
|
|
|
|
function thlcgwasm.getregisterfordef(list: TAsmList;size:tdef):Tregister;
|
|
begin
|
|
case def2regtyp(size) of
|
|
R_EXTERNREFREGISTER:
|
|
result:=TCgWasm(cg).getexternrefregister(list);
|
|
R_FUNCREFREGISTER:
|
|
result:=TCgWasm(cg).getfuncrefregister(list);
|
|
else
|
|
result:=inherited;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure thlcgwasm.a_load_const_cgpara(list: TAsmList; tosize: tdef; a: tcgint; const cgpara: TCGPara);
|
|
begin
|
|
tosize:=get_para_push_size(tosize);
|
|
if tosize=s8inttype then
|
|
a:=shortint(a)
|
|
else if tosize=s16inttype then
|
|
a:=smallint(a);
|
|
inherited a_load_const_cgpara(list, tosize, a, cgpara);
|
|
end;
|
|
|
|
function thlcgwasm.a_call_name(list: TAsmList; pd: tprocdef; const s: TSymStr; const paras: array of pcgpara; forceresdef: tdef; weak: boolean): tcgpara;
|
|
begin
|
|
list.concat(taicpu.op_sym_functype(a_call,current_asmdata.RefAsmSymbol(s,AT_FUNCTION),tcpuprocdef(pd).create_functype));
|
|
result:=get_call_result_cgpara(pd,forceresdef);
|
|
end;
|
|
|
|
|
|
function thlcgwasm.a_call_reg(list: TAsmList; pd: tabstractprocdef; reg: tregister; const paras: array of pcgpara): tcgpara;
|
|
begin
|
|
a_load_reg_stack(list, ptrsinttype, reg);
|
|
if pd.typ=procvardef then
|
|
current_asmdata.CurrAsmList.Concat(taicpu.op_functype(a_call_indirect,tcpuprocvardef(pd).create_functype))
|
|
else
|
|
current_asmdata.CurrAsmList.Concat(taicpu.op_functype(a_call_indirect,tcpuprocdef(pd).create_functype));
|
|
decstack(list,1);
|
|
result:=hlcg.get_call_result_cgpara(pd, nil);
|
|
end;
|
|
|
|
|
|
procedure thlcgwasm.a_load_const_stack(list : TAsmList;size : tdef;a : tcgint; typ: TRegisterType);
|
|
begin
|
|
case typ of
|
|
R_INTREGISTER,
|
|
R_ADDRESSREGISTER:
|
|
begin
|
|
case def_cgsize(size) of
|
|
OS_8,OS_16,OS_32,
|
|
OS_S8,OS_S16,OS_S32:
|
|
begin
|
|
{ convert cardinals to longints }
|
|
list.concat(taicpu.op_const(a_i32_const, a));
|
|
end;
|
|
OS_64,OS_S64:
|
|
begin
|
|
list.concat(taicpu.op_const(a_i64_const, a));
|
|
end;
|
|
else
|
|
internalerror(2010110702);
|
|
end;
|
|
end;
|
|
R_EXTERNREFREGISTER:
|
|
begin
|
|
if a<>0 then
|
|
internalerror(2023061101);
|
|
list.Concat(taicpu.op_none(a_ref_null_externref));
|
|
end;
|
|
R_FUNCREFREGISTER:
|
|
begin
|
|
if a<>0 then
|
|
internalerror(2023061102);
|
|
list.Concat(taicpu.op_none(a_ref_null_funcref));
|
|
end;
|
|
else
|
|
internalerror(2010110703);
|
|
end;
|
|
incstack(list,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadaddr_ref_stack(list : TAsmList;fromsize, tosize : tdef;const ref : treference);
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
{ you can't take the address of references, that are on the local stack }
|
|
if (ref.base=NR_EVAL_STACK_BASE) or (ref.index=NR_EVAL_STACK_BASE) or
|
|
(ref.base=NR_LOCAL_STACK_POINTER_REG) or (ref.index=NR_LOCAL_STACK_POINTER_REG) then
|
|
internalerror(2021010101);
|
|
|
|
tmpref:=ref;
|
|
tmpref.base:=NR_NO;
|
|
tmpref.index:=NR_NO;
|
|
if tmpref.refaddr=addr_got_tls then
|
|
begin
|
|
tmpref.offset:=0;
|
|
list.Concat(taicpu.op_ref(a_global_get, tmpref));
|
|
incstack(list, 1);
|
|
if ref.offset<>0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.offset));
|
|
incstack(list, 1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list, 1);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
list.Concat(taicpu.op_ref(a_i32_const, tmpref));
|
|
incstack(list, 1);
|
|
end;
|
|
if ref.base<>NR_NO then
|
|
begin
|
|
list.Concat(taicpu.op_reg(a_local_get,ref.base));
|
|
incstack(list, 1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list, 1);
|
|
end;
|
|
if ref.index<>NR_NO then
|
|
begin
|
|
list.Concat(taicpu.op_reg(a_local_get,ref.index));
|
|
incstack(list, 1);
|
|
if ref.scalefactor>1 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.scalefactor));
|
|
incstack(list, 1);
|
|
list.Concat(taicpu.op_none(a_i32_mul));
|
|
decstack(list, 1);
|
|
end;
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list, 1);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_stack_loc(list: TAsmList; size: tdef; const loc: tlocation);
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
case loc.loc of
|
|
LOC_REGISTER,LOC_CREGISTER,
|
|
LOC_FPUREGISTER,LOC_CFPUREGISTER:
|
|
a_load_stack_reg(list,size,loc.register);
|
|
LOC_REFERENCE:
|
|
begin
|
|
tmpref:=loc.reference;
|
|
a_load_stack_ref(list,size,loc.reference,prepare_stack_for_ref(list,tmpref,false));
|
|
end;
|
|
else
|
|
internalerror(2011020501);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_loc_stack(list: TAsmList;size: tdef;const loc: tlocation);
|
|
var
|
|
tmpref: treference;
|
|
extra_slots: LongInt;
|
|
begin
|
|
case loc.loc of
|
|
LOC_REGISTER,LOC_CREGISTER,
|
|
LOC_FPUREGISTER,LOC_CFPUREGISTER:
|
|
a_load_reg_stack(list,size,loc.register);
|
|
LOC_REFERENCE,LOC_CREFERENCE:
|
|
begin
|
|
tmpref:=loc.reference;
|
|
extra_slots:=prepare_stack_for_ref(list,tmpref,false);
|
|
a_load_ref_stack(list,size,tmpref,extra_slots);
|
|
end;
|
|
LOC_CONSTANT:
|
|
a_load_const_stack(list,size,loc.value,def2regtyp(size));
|
|
LOC_SUBSETREF,LOC_CSUBSETREF:
|
|
a_load_subsetref_stack(list,size,loc.sref);
|
|
else
|
|
internalerror(2011010401);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadfpu_const_stack(list: TAsmList; size: tdef; a: double);
|
|
begin
|
|
case tfloatdef(size).floattype of
|
|
s32real:
|
|
begin
|
|
list.concat(taicpu.op_single(a_f32_const, a));
|
|
incstack(list,1);
|
|
end;
|
|
s64real:
|
|
begin
|
|
list.concat(taicpu.op_double(a_f64_const,a));
|
|
incstack(list,1);
|
|
end
|
|
else
|
|
internalerror(2011010501);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_stack(list: TAsmList; op: topcg; size: tdef);
|
|
begin
|
|
case def_cgsize(size) of
|
|
OS_8,OS_S8,
|
|
OS_16,OS_S16,
|
|
OS_32,OS_S32:
|
|
begin
|
|
{ boolean not: =0? for boolean }
|
|
if (op=OP_NOT) and is_pasbool(size) then
|
|
list.concat(taicpu.op_none(a_i32_eqz))
|
|
else if (op=OP_NOT) and is_cbool(size) then
|
|
begin
|
|
current_asmdata.CurrAsmList.Concat(taicpu.op_functype(a_if,TWasmFuncType.Create([],[wbt_i32])));
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_const(a_i32_const, 0) );
|
|
incstack(current_asmdata.CurrAsmList,1);
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_none(a_else) );
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
case def_cgsize(size) of
|
|
OS_32,OS_S32:
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_const(a_i32_const, -1) );
|
|
OS_16,OS_S16:
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_const(a_i32_const, 65535) );
|
|
OS_8,OS_S8:
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_const(a_i32_const, 255) );
|
|
else
|
|
internalerror(2021100102);
|
|
end;
|
|
incstack(current_asmdata.CurrAsmList,1);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
|
|
end
|
|
else
|
|
begin
|
|
if op=OP_NOT then
|
|
begin
|
|
{ not = xor -1 for integer }
|
|
a_load_const_stack(list,s32inttype,high(cardinal),R_INTREGISTER);
|
|
op:=OP_XOR;
|
|
end
|
|
else if op=OP_NEG then
|
|
begin
|
|
{ neg = *(-1) }
|
|
a_load_const_stack(list,s32inttype,-1,R_INTREGISTER);
|
|
op:=OP_MUL;
|
|
end;
|
|
if TOpCG2IAsmOp[op]=A_None then
|
|
internalerror(2010120532);
|
|
list.concat(taicpu.op_none(TOpCG2IAsmOp[op]));
|
|
decstack(list,1);
|
|
end;
|
|
maybe_adjust_op_result(list,op,size);
|
|
end;
|
|
OS_64,OS_S64:
|
|
begin
|
|
{ boolean not: =0? for boolean }
|
|
if (op=OP_NOT) and is_pasbool(size) then
|
|
begin
|
|
list.concat(taicpu.op_none(a_i64_eqz));
|
|
list.concat(taicpu.op_none(a_i64_extend_i32_u));
|
|
end
|
|
else if (op=OP_NOT) and is_cbool(size) then
|
|
begin
|
|
list.concat(taicpu.op_none(a_i64_eqz));
|
|
current_asmdata.CurrAsmList.Concat(taicpu.op_functype(a_if,TWasmFuncType.Create([],[wbt_i64])));
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_const(a_i64_const, -1) );
|
|
incstack(current_asmdata.CurrAsmList,1);
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_none(a_else) );
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
current_asmdata.CurrAsmList.Concat( taicpu.op_const(a_i64_const, 0) );
|
|
incstack(current_asmdata.CurrAsmList,1);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
|
|
end
|
|
else
|
|
begin
|
|
if op=OP_NOT then
|
|
begin
|
|
{ not = xor -1 for integer }
|
|
a_load_const_stack(list,s64inttype,-1,R_INTREGISTER);
|
|
op:=OP_XOR;
|
|
end
|
|
else if op=OP_NEG then
|
|
begin
|
|
{ neg = *(-1) }
|
|
a_load_const_stack(list,s64inttype,-1,R_INTREGISTER);
|
|
op:=OP_MUL;
|
|
end;
|
|
if TOpCG2LAsmOp[op]=A_None then
|
|
internalerror(2010120533);
|
|
list.concat(taicpu.op_none(TOpCG2LAsmOp[op]));
|
|
decstack(list,1);
|
|
end;
|
|
end;
|
|
else
|
|
internalerror(2010120531);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_const_stack(list: TAsmList;op: topcg;size: tdef;a: tcgint);
|
|
begin
|
|
case op of
|
|
OP_NEG,OP_NOT:
|
|
internalerror(2011010801);
|
|
else
|
|
a_load_const_stack(list,size,a,R_INTREGISTER);
|
|
end;
|
|
a_op_stack(list,op,size);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_reg_stack(list: TAsmList; op: topcg; size: tdef; reg: tregister);
|
|
begin
|
|
a_load_reg_stack(list,size,reg);
|
|
a_op_stack(list,op,size);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_ref_stack(list: TAsmList; op: topcg; size: tdef; const ref: treference);
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
{ ref must not be the stack top, because that may indicate an error
|
|
(it means that we will perform an operation of the stack top onto
|
|
itself, so that means the two values have been loaded manually prior
|
|
to calling this routine, instead of letting this routine load one of
|
|
them; if something like that is needed, call a_op_stack() directly) }
|
|
if ref.base=NR_EVAL_STACK_BASE then
|
|
internalerror(2010121102);
|
|
tmpref:=ref;
|
|
a_load_ref_stack(list,size,tmpref,prepare_stack_for_ref(list,tmpref,false));
|
|
a_op_stack(list,op,size);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_loc_stack(list: TAsmList; op: topcg; size: tdef; const loc: tlocation);
|
|
begin
|
|
case loc.loc of
|
|
LOC_REGISTER,LOC_CREGISTER:
|
|
a_op_reg_stack(list,op,size,loc.register);
|
|
LOC_REFERENCE,LOC_CREFERENCE:
|
|
a_op_ref_stack(list,op,size,loc.reference);
|
|
LOC_CONSTANT:
|
|
a_op_const_stack(list,op,size,loc.value);
|
|
else
|
|
internalerror(2011011415)
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_loc_stack(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; const loc: tlocation);
|
|
var
|
|
tmpreg: tregister;
|
|
begin
|
|
case loc.loc of
|
|
LOC_REGISTER,LOC_CREGISTER:
|
|
a_cmp_const_reg_stack(list,size,cmp_op,a,loc.register);
|
|
LOC_REFERENCE,LOC_CREFERENCE:
|
|
a_cmp_const_ref_stack(list,size,cmp_op,a,loc.reference);
|
|
LOC_SUBSETREG, LOC_CSUBSETREG:
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_subsetreg_reg(list,size,size,loc.sreg,tmpreg);
|
|
a_cmp_const_reg_stack(list,size,cmp_op,a,tmpreg);
|
|
end;
|
|
LOC_SUBSETREF, LOC_CSUBSETREF:
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_subsetref_reg(list,size,size,loc.sref,tmpreg);
|
|
a_cmp_const_reg_stack(list,size,cmp_op,a,tmpreg);
|
|
end;
|
|
else
|
|
internalerror(2010120430);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_ref_stack(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; const ref: treference);
|
|
var
|
|
tmpref: treference;
|
|
regtyp: TRegisterType;
|
|
begin
|
|
tmpref:=ref;
|
|
if tmpref.base<>NR_EVAL_STACK_BASE then
|
|
a_load_ref_stack(list,size,tmpref,prepare_stack_for_ref(list,tmpref,false));
|
|
regtyp:=def2regtyp(size);
|
|
case regtyp of
|
|
R_EXTERNREFREGISTER,
|
|
R_FUNCREFREGISTER:
|
|
begin
|
|
if a<>0 then
|
|
internalerror(2023061103);
|
|
if not (cmp_op in [OC_EQ,OC_NE]) then
|
|
internalerror(2023061104);
|
|
list.Concat(taicpu.op_none(a_ref_is_null));
|
|
if cmp_op=OC_NE then
|
|
begin
|
|
a_load_const_stack(list,s32inttype,0,R_INTREGISTER);
|
|
a_cmp_stack_stack(list,s32inttype,OC_EQ);
|
|
end;
|
|
end;
|
|
else
|
|
begin
|
|
a_load_const_stack(list,size,a,regtyp);
|
|
a_cmp_stack_stack(list,size,cmp_op);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_reg_stack(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; reg: tregister);
|
|
var
|
|
regtyp: TRegisterType;
|
|
begin
|
|
a_load_reg_stack(list,size,reg);
|
|
regtyp:=def2regtyp(size);
|
|
case regtyp of
|
|
R_EXTERNREFREGISTER,
|
|
R_FUNCREFREGISTER:
|
|
begin
|
|
if a<>0 then
|
|
internalerror(2023061105);
|
|
if not (cmp_op in [OC_EQ,OC_NE]) then
|
|
internalerror(2023061106);
|
|
list.Concat(taicpu.op_none(a_ref_is_null));
|
|
if cmp_op=OC_NE then
|
|
begin
|
|
a_load_const_stack(list,s32inttype,0,R_INTREGISTER);
|
|
a_cmp_stack_stack(list,s32inttype,OC_EQ);
|
|
end;
|
|
end;
|
|
else
|
|
begin
|
|
a_load_const_stack(list,size,a,regtyp);
|
|
a_cmp_stack_stack(list,size,cmp_op);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_ref_reg_stack(list: TAsmList; size: tdef; cmp_op: topcmp; const ref: treference; reg: tregister);
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
tmpref:=ref;
|
|
a_load_reg_stack(list,size,reg);
|
|
if tmpref.base<>NR_EVAL_STACK_BASE then
|
|
a_load_ref_stack(list,size,tmpref,prepare_stack_for_ref(list,tmpref,false))
|
|
else
|
|
cmp_op:=swap_opcmp(cmp_op);
|
|
a_cmp_stack_stack(list,size,cmp_op);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_ref_stack(list: TAsmList; size: tdef; cmp_op: topcmp; reg: tregister; const ref: treference);
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
tmpref:=ref;
|
|
if tmpref.base<>NR_EVAL_STACK_BASE then
|
|
a_load_ref_stack(list,size,ref,prepare_stack_for_ref(list,tmpref,false));
|
|
a_load_reg_stack(list,size,reg);
|
|
a_cmp_stack_stack(list,size,cmp_op);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_reg_stack(list: TAsmList; size: tdef; cmp_op: topcmp; reg1, reg2: tregister);
|
|
begin
|
|
a_load_reg_stack(list,size,reg2);
|
|
a_load_reg_stack(list,size,reg1);
|
|
a_cmp_stack_stack(list,size,cmp_op);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_subsetreg_reg_stack(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sreg: tsubsetregister; reg: tregister);
|
|
var
|
|
tmpreg: tregister;
|
|
begin
|
|
tmpreg:=getintregister(list,cmpsize);
|
|
a_load_subsetreg_reg(list,fromsubsetsize,cmpsize,sreg,tmpreg);
|
|
a_cmp_reg_reg_stack(list,cmpsize,cmp_op,tmpreg,reg);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_subsetref_reg_stack(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sref: tsubsetreference; reg: tregister);
|
|
var
|
|
tmpreg: tregister;
|
|
begin
|
|
tmpreg:=getintregister(list,cmpsize);
|
|
a_load_subsetref_reg(list,fromsubsetsize,cmpsize,sref,tmpreg);
|
|
a_cmp_reg_reg_stack(list,cmpsize,cmp_op,tmpreg,reg);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_loc_reg_stack(list: TAsmList; size: tdef; cmp_op: topcmp; const loc: tlocation; reg: tregister);
|
|
begin
|
|
case loc.loc of
|
|
LOC_REGISTER,
|
|
LOC_CREGISTER:
|
|
a_cmp_reg_reg_stack(list,size,cmp_op,loc.register,reg);
|
|
LOC_REFERENCE,
|
|
LOC_CREFERENCE :
|
|
a_cmp_ref_reg_stack(list,size,cmp_op,loc.reference,reg);
|
|
LOC_CONSTANT:
|
|
a_cmp_const_reg_stack(list,size,cmp_op,loc.value,reg);
|
|
LOC_SUBSETREG,
|
|
LOC_CSUBSETREG:
|
|
a_cmp_subsetreg_reg_stack(list,size,size,cmp_op,loc.sreg,reg);
|
|
LOC_SUBSETREF,
|
|
LOC_CSUBSETREF:
|
|
a_cmp_subsetref_reg_stack(list,size,size,cmp_op,loc.sref,reg);
|
|
else
|
|
internalerror(2010120431);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_loc_stack(list: TAsmList; size: tdef; cmp_op: topcmp; reg: tregister; const loc: tlocation);
|
|
begin
|
|
a_cmp_loc_reg_stack(list,size,swap_opcmp(cmp_op),loc,reg);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_ref_loc_stack(list: TAsmList; size: tdef; cmp_op: topcmp; const ref: treference; const loc: tlocation);
|
|
var
|
|
tmpreg: tregister;
|
|
begin
|
|
case loc.loc of
|
|
LOC_REGISTER,LOC_CREGISTER:
|
|
a_cmp_ref_reg_stack(list,size,cmp_op,ref,loc.register);
|
|
LOC_REFERENCE,LOC_CREFERENCE:
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_ref_reg(list,size,size,loc.reference,tmpreg);
|
|
a_cmp_ref_reg_stack(list,size,cmp_op,ref,tmpreg);
|
|
end;
|
|
LOC_CONSTANT:
|
|
begin
|
|
a_cmp_const_ref_stack(list,size,swap_opcmp(cmp_op),loc.value,ref);
|
|
end;
|
|
LOC_SUBSETREG, LOC_CSUBSETREG:
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_ref_reg(list,size,size,loc.reference,tmpreg);
|
|
a_cmp_subsetreg_reg_stack(list,size,size,swap_opcmp(cmp_op),loc.sreg,tmpreg);
|
|
end;
|
|
LOC_SUBSETREF, LOC_CSUBSETREF:
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_ref_reg(list,size,size,loc.reference,tmpreg);
|
|
a_cmp_subsetref_reg_stack(list,size,size,swap_opcmp(cmp_op),loc.sref,tmpreg);
|
|
end;
|
|
else
|
|
internalerror(2010120432);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_loc_br(list: TAsmList; size: tdef;cmp_op: topcmp; a: tcgint; const loc: tlocation; br: Integer);
|
|
begin
|
|
a_cmp_const_loc_stack(list,size,cmp_op,a,loc);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_ref_br(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; const ref: treference; br: Integer);
|
|
begin
|
|
a_cmp_const_ref_stack(list,size,cmp_op,a,ref);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_reg_br(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; reg: tregister; br: Integer);
|
|
begin
|
|
a_cmp_const_reg_stack(list,size,cmp_op,a,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_ref_reg_br(list: TAsmList; size: tdef; cmp_op: topcmp; const ref: treference; reg: tregister; br: Integer);
|
|
begin
|
|
a_cmp_ref_reg_stack(list,size,cmp_op,ref,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_ref_br(list: TAsmList; size: tdef; cmp_op: topcmp; reg: tregister; const ref: treference; br: Integer);
|
|
begin
|
|
a_cmp_reg_ref_stack(list,size,cmp_op,reg,ref);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_reg_br(list: TAsmList; size: tdef; cmp_op: topcmp; reg1, reg2: tregister; br: Integer);
|
|
begin
|
|
a_cmp_reg_reg_stack(list,size,cmp_op,reg1,reg2);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_subsetreg_reg_br(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sreg: tsubsetregister; reg: tregister; br: Integer);
|
|
begin
|
|
a_cmp_subsetreg_reg_stack(list,fromsubsetsize,cmpsize,cmp_op,sreg,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_subsetref_reg_br(list: TAsmList; fromsubsetsize, cmpsize: tdef; cmp_op: topcmp; const sref: tsubsetreference; reg: tregister; br: Integer);
|
|
begin
|
|
a_cmp_subsetref_reg_stack(list,fromsubsetsize,cmpsize,cmp_op,sref,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_loc_reg_br(list : TAsmList;size : tdef;cmp_op : topcmp; const loc: tlocation; reg : tregister; br: Integer);
|
|
begin
|
|
a_cmp_loc_reg_stack(list,size,cmp_op,loc,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_loc_br(list : TAsmList;size : tdef;cmp_op : topcmp; reg: tregister; const loc: tlocation; br: Integer);
|
|
begin
|
|
a_cmp_reg_loc_stack(list,size,cmp_op,reg,loc);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_ref_loc_br(list: TAsmList; size: tdef;cmp_op: topcmp; const ref: treference; const loc: tlocation; br: Integer);
|
|
begin
|
|
a_cmp_ref_loc_stack(list,size,cmp_op,ref,loc);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,br));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation);
|
|
begin
|
|
case fromloc.loc of
|
|
LOC_CREFERENCE,
|
|
LOC_REFERENCE:
|
|
begin
|
|
toloc:=fromloc;
|
|
if (fromloc.reference.base<>NR_NO) and
|
|
(fromloc.reference.base<>current_procinfo.framepointer) and
|
|
(fromloc.reference.base<>NR_STACK_POINTER_REG) then
|
|
g_allocload_reg_reg(list,voidpointertype,fromloc.reference.base,toloc.reference.base,R_ADDRESSREGISTER);
|
|
end;
|
|
else
|
|
inherited;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_stack_stack(list: TAsmlist; size: tdef; cmp_op: topcmp);
|
|
const
|
|
opcmp32: array[topcmp] of tasmop = (
|
|
A_None, { OC_NONE, }
|
|
a_i32_eq, { OC_EQ, equality comparison }
|
|
a_i32_gt_s, { OC_GT, greater than (signed) }
|
|
a_i32_lt_s, { OC_LT, less than (signed) }
|
|
a_i32_ge_s, { OC_GTE, greater or equal than (signed) }
|
|
a_i32_le_s, { OC_LTE, less or equal than (signed) }
|
|
a_i32_ne, { OC_NE, not equal }
|
|
a_i32_le_u, { OC_BE, less or equal than (unsigned) }
|
|
a_i32_lt_u, { OC_B, less than (unsigned) }
|
|
a_i32_ge_u, { OC_AE, greater or equal than (unsigned) }
|
|
a_i32_gt_u { OC_A greater than (unsigned) }
|
|
);
|
|
const
|
|
opcmp64: array[TOpCmp] of TAsmOp = (A_None,
|
|
a_i64_eq, // OC_EQ
|
|
a_i64_gt_s, a_i64_lt_s, // OC_GT, OC_LT
|
|
a_i64_ge_s, a_i64_le_s, // OC_GTE, OC_LTE
|
|
a_i64_ne, // OC_NE
|
|
a_i64_le_u, a_i64_lt_u, // OC_BE, OC_B
|
|
a_i64_ge_u, a_i64_gt_u // OC_AE, OC_A
|
|
);
|
|
|
|
var
|
|
cgsize: tcgsize;
|
|
begin
|
|
case def2regtyp(size) of
|
|
R_INTREGISTER,
|
|
R_ADDRESSREGISTER:
|
|
begin
|
|
cgsize:=def_cgsize(size);
|
|
case cgsize of
|
|
OS_S8,OS_8,
|
|
OS_16,OS_S16,
|
|
OS_S32,OS_32:
|
|
begin
|
|
list.concat(taicpu.op_none(opcmp32[cmp_op]));
|
|
decstack(list,1);
|
|
end;
|
|
OS_64,OS_S64:
|
|
begin
|
|
list.concat(taicpu.op_none(opcmp64[cmp_op]));
|
|
decstack(list,1);
|
|
end;
|
|
else
|
|
internalerror(2010120538);
|
|
end;
|
|
end;
|
|
else
|
|
internalerror(2010120538);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.maybe_adjust_op_result(list: TAsmList; op: TOpCg; size: tdef);
|
|
const
|
|
overflowops = [OP_MUL,OP_SHL,OP_ADD,OP_SUB,OP_NOT,OP_NEG];
|
|
begin
|
|
if (op in overflowops) and
|
|
(def_cgsize(size) in [OS_8,OS_S8,OS_16,OS_S16]) then
|
|
resize_stack_int_val(list,s32inttype,size,false);
|
|
end;
|
|
|
|
procedure thlcgwasm.gen_load_uninitialized_function_result(list: TAsmList; pd: tprocdef; resdef: tdef; const resloc: tcgpara);
|
|
begin
|
|
{ nothing to do for ret_in_param results }
|
|
if paramanager.ret_in_param(pd.returndef,pd) then
|
|
exit;
|
|
{ constructors don't return anything in Java }
|
|
if pd.proctypeoption=potype_constructor then
|
|
exit;
|
|
{ must return a value of the correct type on the evaluation stack }
|
|
case def2regtyp(resdef) of
|
|
R_INTREGISTER,
|
|
R_ADDRESSREGISTER:
|
|
a_load_const_cgpara(list,resdef,0,resloc);
|
|
R_FPUREGISTER:
|
|
case tfloatdef(resdef).floattype of
|
|
s32real:
|
|
begin
|
|
list.concat(taicpu.op_single(a_f32_const, 0));
|
|
incstack(list,1);
|
|
end;
|
|
s64real:
|
|
begin
|
|
list.concat(taicpu.op_double(a_f64_const, 0));
|
|
incstack(list,1);
|
|
end;
|
|
else
|
|
internalerror(2011010302);
|
|
end;
|
|
R_FUNCREFREGISTER:
|
|
begin
|
|
list.concat(taicpu.op_none(a_ref_null_funcref));
|
|
incstack(list,1);
|
|
end;
|
|
R_EXTERNREFREGISTER:
|
|
begin
|
|
list.concat(taicpu.op_none(a_ref_null_externref));
|
|
incstack(list,1);
|
|
end;
|
|
else
|
|
internalerror(2011010301);
|
|
end;
|
|
end;
|
|
|
|
|
|
function thlcgwasm.g_call_system_proc_intern(list: TAsmList; pd: tprocdef; const paras: array of pcgpara; forceresdef: tdef): tcgpara;
|
|
begin
|
|
result:=inherited;
|
|
pd.init_paraloc_info(callerside);
|
|
g_adjust_stack_after_call(list,pd);
|
|
end;
|
|
|
|
|
|
function thlcgwasm.prepare_stack_for_ref(list: TAsmList; var ref: treference; dup: boolean): longint;
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
result:=0;
|
|
{ fake location that indicates the value is already on the stack? }
|
|
if (ref.base=NR_EVAL_STACK_BASE) or (ref.base=NR_LOCAL_STACK_POINTER_REG) then
|
|
exit;
|
|
|
|
if (ref.base=NR_NO) and (ref.index<>NR_NO) and (ref.scalefactor<=1) then
|
|
begin
|
|
ref.base:=ref.index;
|
|
ref.index:=NR_NO;
|
|
end;
|
|
|
|
if assigned(ref.symbol) and (ref.symbol.typ=AT_WASM_GLOBAL) then
|
|
begin
|
|
if ref.base<>NR_NO then
|
|
internalerror(2022072601);
|
|
if ref.index<>NR_NO then
|
|
internalerror(2022072602);
|
|
if ref.offset<>0 then
|
|
internalerror(2022072603);
|
|
end
|
|
else if ref.refaddr=addr_got_tls then
|
|
begin
|
|
if not assigned(ref.symbol) then
|
|
internalerror(2022071405);
|
|
if ref.base<>NR_NO then
|
|
internalerror(2022071406);
|
|
if ref.index<>NR_NO then
|
|
internalerror(2022071407);
|
|
tmpref:=ref;
|
|
tmpref.offset:=0;
|
|
list.Concat(taicpu.op_ref(a_global_get,tmpref));
|
|
incstack(list,1);
|
|
if dup then
|
|
begin
|
|
list.Concat(taicpu.op_ref(a_global_get,tmpref));
|
|
incstack(list,1);
|
|
end;
|
|
result:=1;
|
|
end
|
|
else if assigned(ref.symbol) and (ref.base=NR_NO) and (ref.index=NR_NO) then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.offset));
|
|
incstack(list,1);
|
|
if dup then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.offset));
|
|
incstack(list,1);
|
|
end;
|
|
ref.offset:=0;
|
|
result:=1;
|
|
end
|
|
else if ref.index <> NR_NO then // array access
|
|
begin
|
|
// it's just faster to sum two of those together
|
|
list.Concat(taicpu.op_reg(a_local_get, ref.base));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_reg(a_local_get, ref.index));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
if assigned(ref.symbol) then
|
|
begin
|
|
list.Concat(taicpu.op_sym(a_i32_const,ref.symbol));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
if ref.offset<0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,-ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_sub));
|
|
decstack(list,1);
|
|
end
|
|
else if ref.offset>0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
if dup then
|
|
begin
|
|
list.Concat(taicpu.op_reg(a_local_get, ref.base));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_reg(a_local_get, ref.index));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
if assigned(ref.symbol) then
|
|
begin
|
|
list.Concat(taicpu.op_sym(a_i32_const,ref.symbol));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
if ref.offset<0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,-ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_sub));
|
|
decstack(list,1);
|
|
end
|
|
else if ref.offset>0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
end;
|
|
ref.base:=NR_NO;
|
|
ref.index:=NR_NO;
|
|
ref.offset:=0;
|
|
ref.symbol:=nil;
|
|
result:=1;
|
|
end
|
|
else if (ref.base<>NR_NO) then
|
|
begin
|
|
if (ref.base<>NR_STACK_POINTER_REG) then
|
|
begin
|
|
{ regular field -> load self on the stack }
|
|
a_load_reg_stack(list,voidpointertype,ref.base);
|
|
if assigned(ref.symbol) then
|
|
begin
|
|
list.Concat(taicpu.op_sym(a_i32_const,ref.symbol));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
if ref.offset<0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,-ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_sub));
|
|
decstack(list,1);
|
|
end
|
|
else if ref.offset>0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
if dup then
|
|
begin
|
|
a_load_reg_stack(list,voidpointertype,ref.base);
|
|
if assigned(ref.symbol) then
|
|
begin
|
|
list.Concat(taicpu.op_sym(a_i32_const,ref.symbol));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
if ref.offset<0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,-ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_sub));
|
|
decstack(list,1);
|
|
end
|
|
else if ref.offset>0 then
|
|
begin
|
|
list.Concat(taicpu.op_const(a_i32_const,ref.offset));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_none(a_i32_add));
|
|
decstack(list,1);
|
|
end;
|
|
end;
|
|
ref.offset:=0;
|
|
ref.symbol:=nil;
|
|
ref.base:=NR_NO;
|
|
result:=1;
|
|
end
|
|
else // if (ref.base = NR_FRAME_POINTER_REG) then
|
|
begin
|
|
internalerror(2021012202);
|
|
//list.Concat(taicpu.op_sym(a_local_get, current_asmdata.RefAsmSymbol(FRAME_POINTER_SYM,AT_ADDR) ));
|
|
//incstack(list,1);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
{ static field -> nothing to do here, except for validity check }
|
|
{if not assigned(ref.symbol) or
|
|
(ref.offset<>0) then
|
|
begin
|
|
internalerror(2010120525);
|
|
end;}
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.gen_load_cgpara_loc(list: TAsmList; vardef: tdef; const para: TCGPara; var destloc: tlocation; reusepara: boolean);
|
|
begin
|
|
{ support loading a function result (from the evaluation stack), to a register }
|
|
if assigned(para.location) and (not assigned(para.location^.next)) and
|
|
(para.location^.loc in [LOC_REFERENCE,LOC_CREFERENCE]) and
|
|
(para.location^.reference.index=NR_EVAL_STACK_BASE) and
|
|
(para.location^.reference.offset=0) and
|
|
(def_cgsize(para.location^.Def)=destloc.size) and
|
|
(destloc.loc=LOC_REGISTER) then
|
|
a_load_stack_loc(list,para.location^.Def,destloc)
|
|
else
|
|
inherited;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_const_reg(list: TAsmList; tosize: tdef; a: tcgint; register: tregister);
|
|
begin
|
|
a_load_const_stack(list,tosize,a,def2regtyp(tosize));
|
|
a_load_stack_reg(list,tosize,register);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_const_ref(list: TAsmList; tosize: tdef; a: tcgint; const ref: treference);
|
|
var
|
|
extra_slots: longint;
|
|
tmpref: treference;
|
|
begin
|
|
g_load_check_simple(list,ref,1024);
|
|
tmpref:=ref;
|
|
extra_slots:=prepare_stack_for_ref(list,tmpref,false);
|
|
a_load_const_stack(list,tosize,a,def2regtyp(tosize));
|
|
a_load_stack_ref(list,tosize,tmpref,extra_slots);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_reg_ref(list: TAsmList; fromsize, tosize: tdef; register: tregister; const ref: treference);
|
|
var
|
|
extra_slots: longint;
|
|
tmpref: treference;
|
|
begin
|
|
g_load_check_simple(list,ref,1024);
|
|
tmpref:=ref;
|
|
extra_slots:=prepare_stack_for_ref(list,tmpref,false);
|
|
a_load_reg_stack(list,fromsize,register);
|
|
if def2regtyp(fromsize)=R_INTREGISTER then
|
|
resize_stack_int_val(list,fromsize,tosize,assigned(tmpref.symbol));
|
|
a_load_stack_ref(list,tosize,tmpref,extra_slots);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister);
|
|
begin
|
|
a_load_reg_stack(list,fromsize,reg1);
|
|
if def2regtyp(fromsize)=R_INTREGISTER then
|
|
resize_stack_int_val(list,fromsize,tosize,false);
|
|
a_load_stack_reg(list,tosize,reg2);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; register: tregister);
|
|
var
|
|
extra_slots: longint;
|
|
tmpref: treference;
|
|
begin
|
|
g_load_check_simple(list,ref,1024);
|
|
tmpref:=ref;
|
|
extra_slots:=prepare_stack_for_ref(list,tmpref,false);
|
|
a_load_ref_stack(list,fromsize,tmpref,extra_slots);
|
|
|
|
if def2regtyp(fromsize)=R_INTREGISTER then
|
|
resize_stack_int_val(list,fromsize,tosize,false);
|
|
a_load_stack_reg(list,tosize,register);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_ref_ref(list: TAsmList; fromsize, tosize: tdef; const sref: treference; const dref: treference);
|
|
var
|
|
extra_sslots,
|
|
extra_dslots: longint;
|
|
tmpsref, tmpdref: treference;
|
|
tmpreg: tregister;
|
|
begin
|
|
if sref.base<>NR_EVAL_STACK_BASE then
|
|
begin
|
|
g_load_check_simple(list,sref,1024);
|
|
g_load_check_simple(list,dref,1024);
|
|
tmpsref:=sref;
|
|
tmpdref:=dref;
|
|
{ make sure the destination reference is on top, since in the end the
|
|
order has to be "destref, value" -> first create "destref, sourceref" }
|
|
extra_dslots:=prepare_stack_for_ref(list,tmpdref,false);
|
|
extra_sslots:=prepare_stack_for_ref(list,tmpsref,false);
|
|
a_load_ref_stack(list,fromsize,tmpsref,extra_sslots);
|
|
if def2regtyp(fromsize)=R_INTREGISTER then
|
|
resize_stack_int_val(list,fromsize,tosize,assigned(tmpdref.symbol));
|
|
a_load_stack_ref(list,tosize,tmpdref,extra_dslots);
|
|
end
|
|
else
|
|
begin
|
|
{ verify if we have the same reference }
|
|
if references_equal(sref,dref) then
|
|
exit;
|
|
tmpreg:=getregisterfordef(list,tosize);
|
|
a_load_ref_reg(list,fromsize,tosize,sref,tmpreg);
|
|
a_load_reg_ref(list,tosize,tosize,tmpreg,dref);
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_loc_ref(list : TAsmList;fromsize, tosize: tdef; const loc: tlocation; const ref : treference);
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
if is_methodptr_like_type(tosize) and (loc.loc in [LOC_REGISTER,LOC_CREGISTER]) then
|
|
begin
|
|
tmpref:=ref;
|
|
a_load_reg_ref(list,voidcodepointertype,voidcodepointertype,loc.register,tmpref);
|
|
inc(tmpref.offset,voidcodepointertype.size);
|
|
{ the second part could be either self or parentfp }
|
|
if tosize.size=(voidcodepointertype.size+voidpointertype.size) then
|
|
a_load_reg_ref(list,voidpointertype,voidpointertype,loc.registerhi,tmpref)
|
|
else if tosize.size=(voidcodepointertype.size+parentfpvoidpointertype.size) then
|
|
a_load_reg_ref(list,parentfpvoidpointertype,parentfpvoidpointertype,loc.registerhi,tmpref)
|
|
else
|
|
internalerror(2021100301);
|
|
end
|
|
else
|
|
inherited;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadaddr_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; r: tregister);
|
|
begin
|
|
a_loadaddr_ref_stack(list,fromsize,tosize,ref);
|
|
a_load_stack_reg(list, tosize, r);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_subsetref_regs_index(list: TAsmList; subsetsize: tdef; loadbitsize: byte; const sref: tsubsetreference; valuereg: tregister);
|
|
var
|
|
tmpref: treference;
|
|
extra_value_reg,
|
|
tmpreg: tregister;
|
|
begin
|
|
tmpreg:=getintregister(list,osuinttype);
|
|
tmpref:=sref.ref;
|
|
inc(tmpref.offset,loadbitsize div 8);
|
|
extra_value_reg:=getintregister(list,osuinttype);
|
|
|
|
a_op_reg_reg(list,OP_SHR,osuinttype,sref.bitindexreg,valuereg);
|
|
|
|
{ ensure we don't load anything past the end of the array }
|
|
a_cmp_const_reg_stack(list,osuinttype,OC_A,loadbitsize-sref.bitlen,sref.bitindexreg);
|
|
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
|
|
{ Y-x = -(Y-x) }
|
|
a_op_const_reg_reg(list,OP_SUB,osuinttype,loadbitsize,sref.bitindexreg,tmpreg);
|
|
a_op_reg_reg(list,OP_NEG,osuinttype,tmpreg,tmpreg);
|
|
|
|
{ load next "loadbitsize" bits of the array }
|
|
a_load_ref_reg(list,cgsize_orddef(int_cgsize(loadbitsize div 8)),osuinttype,tmpref,extra_value_reg);
|
|
|
|
{ tmpreg is in the range 1..<cpu_bitsize>-1 -> always ok }
|
|
a_op_reg_reg(list,OP_SHL,osuinttype,tmpreg,extra_value_reg);
|
|
{ merge }
|
|
a_op_reg_reg(list,OP_OR,osuinttype,extra_value_reg,valuereg);
|
|
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
|
|
|
|
{ sign extend or mask other bits }
|
|
if is_signed(subsetsize) then
|
|
begin
|
|
a_op_const_reg(list,OP_SHL,osuinttype,AIntBits-sref.bitlen,valuereg);
|
|
a_op_const_reg(list,OP_SAR,osuinttype,AIntBits-sref.bitlen,valuereg);
|
|
end
|
|
else
|
|
a_op_const_reg(list,OP_AND,osuinttype,tcgint((aword(1) shl sref.bitlen)-1),valuereg);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_regconst_subsetref_intern(list : TAsmList; fromsize, subsetsize: tdef; fromreg: tregister; const sref: tsubsetreference; slopt: tsubsetloadopt);
|
|
var
|
|
tmpreg, tmpindexreg, valuereg, extra_value_reg, maskreg: tregister;
|
|
tosreg, fromsreg: tsubsetregister;
|
|
tmpref: treference;
|
|
bitmask: aword;
|
|
loadsize: torddef;
|
|
loadbitsize: byte;
|
|
extra_load: boolean;
|
|
begin
|
|
{ the register must be able to contain the requested value }
|
|
if (fromsize.size*8<sref.bitlen) then
|
|
internalerror(2006081613);
|
|
|
|
get_subsetref_load_info(sref,loadsize,extra_load);
|
|
loadbitsize:=loadsize.size*8;
|
|
|
|
{ load the (first part) of the bit sequence }
|
|
valuereg:=getintregister(list,osuinttype);
|
|
a_load_ref_reg(list,loadsize,osuinttype,sref.ref,valuereg);
|
|
|
|
{ constant offset of bit sequence? }
|
|
if not extra_load then
|
|
begin
|
|
if (sref.bitindexreg=NR_NO) then
|
|
begin
|
|
{ use subsetreg routine, it may have been overridden with an optimized version }
|
|
tosreg.subsetreg:=valuereg;
|
|
tosreg.subsetregsize:=def_cgsize(osuinttype);
|
|
{ subsetregs always count bits from right to left }
|
|
tosreg.startbit:=sref.startbit;
|
|
tosreg.bitlen:=sref.bitlen;
|
|
a_load_regconst_subsetreg_intern(list,fromsize,subsetsize,fromreg,tosreg,slopt);
|
|
end
|
|
else
|
|
begin
|
|
if (sref.startbit<>0) then
|
|
internalerror(2006081710);
|
|
{ should be handled by normal code and will give wrong result }
|
|
{ on x86 for the '1 shl bitlen' below }
|
|
if (sref.bitlen=AIntBits) then
|
|
internalerror(2006081711);
|
|
|
|
{ zero the bits we have to insert }
|
|
if (slopt<>SL_SETMAX) then
|
|
begin
|
|
maskreg:=getintregister(list,osuinttype);
|
|
a_load_const_reg(list,osuinttype,tcgint((aword(1) shl sref.bitlen)-1),maskreg);
|
|
a_op_reg_reg(list,OP_SHL,osuinttype,sref.bitindexreg,maskreg);
|
|
a_op_reg_reg(list,OP_NOT,osuinttype,maskreg,maskreg);
|
|
a_op_reg_reg(list,OP_AND,osuinttype,maskreg,valuereg);
|
|
end;
|
|
|
|
{ insert the value }
|
|
if (slopt<>SL_SETZERO) then
|
|
begin
|
|
tmpreg:=getintregister(list,osuinttype);
|
|
if (slopt<>SL_SETMAX) then
|
|
a_load_reg_reg(list,fromsize,osuinttype,fromreg,tmpreg)
|
|
else if (sref.bitlen<>AIntBits) then
|
|
a_load_const_reg(list,osuinttype,tcgint((aword(1) shl sref.bitlen)-1), tmpreg)
|
|
else
|
|
a_load_const_reg(list,osuinttype,-1,tmpreg);
|
|
if not(slopt in [SL_REGNOSRCMASK,SL_SETMAX]) then
|
|
a_op_const_reg(list,OP_AND,osuinttype,tcgint((aword(1) shl sref.bitlen)-1),tmpreg);
|
|
a_op_reg_reg(list,OP_SHL,osuinttype,sref.bitindexreg,tmpreg);
|
|
a_op_reg_reg(list,OP_OR,osuinttype,tmpreg,valuereg);
|
|
end;
|
|
end;
|
|
{ store back to memory }
|
|
tmpreg:=getintregister(list,loadsize);
|
|
a_load_reg_reg(list,osuinttype,loadsize,valuereg,tmpreg);
|
|
a_load_reg_ref(list,loadsize,loadsize,tmpreg,sref.ref);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
{ load next value }
|
|
extra_value_reg:=getintregister(list,osuinttype);
|
|
tmpref:=sref.ref;
|
|
inc(tmpref.offset,loadbitsize div 8);
|
|
|
|
{ should maybe be taken out too, can be done more efficiently }
|
|
{ on e.g. i386 with shld/shrd }
|
|
if (sref.bitindexreg = NR_NO) then
|
|
begin
|
|
a_load_ref_reg(list,loadsize,osuinttype,tmpref,extra_value_reg);
|
|
|
|
fromsreg.subsetreg:=fromreg;
|
|
fromsreg.subsetregsize:=def_cgsize(fromsize);
|
|
tosreg.subsetreg:=valuereg;
|
|
tosreg.subsetregsize:=def_cgsize(osuinttype);
|
|
|
|
{ transfer first part }
|
|
fromsreg.bitlen:=loadbitsize-sref.startbit;
|
|
tosreg.bitlen:=fromsreg.bitlen;
|
|
|
|
{ valuereg must contain the lower bits of the value at bits [startbit..loadbitsize] }
|
|
|
|
{ lower bits of the value ... }
|
|
fromsreg.startbit:=0;
|
|
{ ... to startbit }
|
|
tosreg.startbit:=sref.startbit;
|
|
case slopt of
|
|
SL_SETZERO,
|
|
SL_SETMAX:
|
|
a_load_regconst_subsetreg_intern(list,fromsize,subsetsize,fromreg,tosreg,slopt);
|
|
else
|
|
a_load_subsetreg_subsetreg(list,subsetsize,subsetsize,fromsreg,tosreg);
|
|
end;
|
|
{$ifndef cpuhighleveltarget}
|
|
valuereg:=cg.makeregsize(list,valuereg,def_cgsize(loadsize));
|
|
a_load_reg_ref(list,loadsize,loadsize,valuereg,sref.ref);
|
|
{$else}
|
|
tmpreg:=getintregister(list,loadsize);
|
|
a_load_reg_reg(list,osuinttype,loadsize,valuereg,tmpreg);
|
|
a_load_reg_ref(list,loadsize,loadsize,tmpreg,sref.ref);
|
|
{$endif}
|
|
|
|
{ transfer second part }
|
|
{ extra_value_reg must contain the upper bits of the value at bits [0..bitlen-(loadbitsize-startbit)] }
|
|
|
|
fromsreg.startbit:=fromsreg.bitlen;
|
|
tosreg.startbit:=0;
|
|
tosreg.subsetreg:=extra_value_reg;
|
|
fromsreg.bitlen:=sref.bitlen-fromsreg.bitlen;
|
|
tosreg.bitlen:=fromsreg.bitlen;
|
|
|
|
case slopt of
|
|
SL_SETZERO,
|
|
SL_SETMAX:
|
|
a_load_regconst_subsetreg_intern(list,fromsize,subsetsize,fromreg,tosreg,slopt);
|
|
else
|
|
a_load_subsetreg_subsetreg(list,subsetsize,subsetsize,fromsreg,tosreg);
|
|
end;
|
|
tmpreg:=getintregister(list,loadsize);
|
|
a_load_reg_reg(list,osuinttype,loadsize,extra_value_reg,tmpreg);
|
|
a_load_reg_ref(list,loadsize,loadsize,tmpreg,tmpref);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
if (sref.startbit <> 0) then
|
|
internalerror(2006081812);
|
|
{ should be handled by normal code and will give wrong result }
|
|
{ on x86 for the '1 shl bitlen' below }
|
|
if (sref.bitlen = AIntBits) then
|
|
internalerror(2006081713);
|
|
|
|
{ generate mask to zero the bits we have to insert }
|
|
if (slopt <> SL_SETMAX) then
|
|
begin
|
|
maskreg := getintregister(list,osuinttype);
|
|
a_load_const_reg(list,osuinttype,tcgint((aword(1) shl sref.bitlen)-1),maskreg);
|
|
a_op_reg_reg(list,OP_SHL,osuinttype,sref.bitindexreg,maskreg);
|
|
|
|
a_op_reg_reg(list,OP_NOT,osuinttype,maskreg,maskreg);
|
|
a_op_reg_reg(list,OP_AND,osuinttype,maskreg,valuereg);
|
|
end;
|
|
|
|
{ insert the value }
|
|
if (slopt <> SL_SETZERO) then
|
|
begin
|
|
tmpreg := getintregister(list,osuinttype);
|
|
if (slopt <> SL_SETMAX) then
|
|
a_load_reg_reg(list,fromsize,osuinttype,fromreg,tmpreg)
|
|
else if (sref.bitlen <> AIntBits) then
|
|
a_load_const_reg(list,osuinttype,tcgint((aword(1) shl sref.bitlen) - 1), tmpreg)
|
|
else
|
|
a_load_const_reg(list,osuinttype,-1,tmpreg);
|
|
if not(slopt in [SL_REGNOSRCMASK,SL_SETMAX]) then
|
|
{ mask left over bits }
|
|
a_op_const_reg(list,OP_AND,osuinttype,tcgint((aword(1) shl sref.bitlen)-1),tmpreg);
|
|
a_op_reg_reg(list,OP_SHL,osuinttype,sref.bitindexreg,tmpreg);
|
|
a_op_reg_reg(list,OP_OR,osuinttype,tmpreg,valuereg);
|
|
end;
|
|
tmpreg:=getintregister(list,loadsize);
|
|
a_load_reg_reg(list,osuinttype,loadsize,valuereg,tmpreg);
|
|
a_load_reg_ref(list,loadsize,loadsize,tmpreg,sref.ref);
|
|
|
|
{ make sure we do not read/write past the end of the array }
|
|
a_cmp_const_reg_stack(list,osuinttype,OC_A,loadbitsize-sref.bitlen,sref.bitindexreg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
|
|
a_load_ref_reg(list,loadsize,osuinttype,tmpref,extra_value_reg);
|
|
tmpindexreg:=getintregister(list,osuinttype);
|
|
|
|
{ load current array value }
|
|
if (slopt<>SL_SETZERO) then
|
|
begin
|
|
tmpreg:=getintregister(list,osuinttype);
|
|
if (slopt<>SL_SETMAX) then
|
|
a_load_reg_reg(list,fromsize,osuinttype,fromreg,tmpreg)
|
|
else if (sref.bitlen<>AIntBits) then
|
|
a_load_const_reg(list,osuinttype,tcgint((aword(1) shl sref.bitlen) - 1), tmpreg)
|
|
else
|
|
a_load_const_reg(list,osuinttype,-1,tmpreg);
|
|
end;
|
|
|
|
{ generate mask to zero the bits we have to insert }
|
|
if (slopt<>SL_SETMAX) then
|
|
begin
|
|
maskreg:=getintregister(list,osuinttype);
|
|
|
|
{ Y-x = -(x-Y) }
|
|
a_op_const_reg_reg(list,OP_SUB,osuinttype,loadbitsize,sref.bitindexreg,tmpindexreg);
|
|
a_op_reg_reg(list,OP_NEG,osuinttype,tmpindexreg,tmpindexreg);
|
|
a_load_const_reg(list,osuinttype,tcgint((aword(1) shl sref.bitlen)-1),maskreg);
|
|
a_op_reg_reg(list,OP_SHR,osuinttype,tmpindexreg,maskreg);
|
|
|
|
a_op_reg_reg(list,OP_NOT,osuinttype,maskreg,maskreg);
|
|
a_op_reg_reg(list,OP_AND,osuinttype,maskreg,extra_value_reg);
|
|
end;
|
|
|
|
if (slopt<>SL_SETZERO) then
|
|
begin
|
|
if not(slopt in [SL_REGNOSRCMASK,SL_SETMAX]) then
|
|
a_op_const_reg(list,OP_AND,osuinttype,tcgint((aword(1) shl sref.bitlen)-1),tmpreg);
|
|
a_op_reg_reg(list,OP_SHR,osuinttype,tmpindexreg,tmpreg);
|
|
a_op_reg_reg(list,OP_OR,osuinttype,tmpreg,extra_value_reg);
|
|
end;
|
|
{$ifndef cpuhighleveltarget}
|
|
extra_value_reg:=cg.makeregsize(list,extra_value_reg,def_cgsize(loadsize));
|
|
a_load_reg_ref(list,loadsize,loadsize,extra_value_reg,tmpref);
|
|
{$else}
|
|
tmpreg:=getintregister(list,loadsize);
|
|
a_load_reg_reg(list,osuinttype,loadsize,extra_value_reg,tmpreg);
|
|
a_load_reg_ref(list,loadsize,loadsize,tmpreg,tmpref);
|
|
{$endif}
|
|
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_const_reg(list: TAsmList; Op: TOpCG; size: tdef; a: tcgint; reg: TRegister);
|
|
begin
|
|
a_op_const_reg_reg(list,op,size,a,reg,reg);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tdef; a: tcgint; src, dst: tregister);
|
|
begin
|
|
a_load_reg_stack(list,size,src);
|
|
a_op_const_stack(list,op,size,a);
|
|
a_load_stack_reg(list,size,dst);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_const_ref(list: TAsmList; Op: TOpCG; size: tdef; a: tcgint; const ref: TReference);
|
|
var
|
|
extra_slots: longint;
|
|
tmpref: treference;
|
|
begin
|
|
tmpref:=ref;
|
|
extra_slots:=prepare_stack_for_ref(list,tmpref,true);
|
|
{ TODO, here or in peepholeopt: use iinc when possible }
|
|
a_load_ref_stack(list,size,tmpref,extra_slots);
|
|
a_op_const_stack(list,op,size,a);
|
|
{ for android verifier }
|
|
if (def2regtyp(size)=R_INTREGISTER) and
|
|
(assigned(tmpref.symbol)) then
|
|
resize_stack_int_val(list,size,size,true);
|
|
a_load_stack_ref(list,size,tmpref,extra_slots);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_ref_reg(list: TAsmList; Op: TOpCG; size: tdef; const ref: TReference; reg: TRegister);
|
|
begin
|
|
if not(op in [OP_NOT,OP_NEG]) then
|
|
a_load_reg_stack(list,size,reg);
|
|
a_op_ref_stack(list,op,size,ref);
|
|
a_load_stack_reg(list,size,reg);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tdef; src1, src2, dst: tregister);
|
|
begin
|
|
if not(op in [OP_NOT,OP_NEG]) then
|
|
a_load_reg_stack(list,size,src2);
|
|
a_op_reg_stack(list,op,size,src1);
|
|
a_load_stack_reg(list,size,dst);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_reg_reg(list: TAsmList; Op: TOpCG; size: tdef; reg1, reg2: TRegister);
|
|
begin
|
|
a_op_reg_reg_reg(list,op,size,reg1,reg2,reg2);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; a: tcgint; src, dst: tregister; setflags: boolean; var ovloc: tlocation);
|
|
var
|
|
tmpreg: tregister;
|
|
begin
|
|
if not setflags then
|
|
begin
|
|
inherited;
|
|
exit;
|
|
end;
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_const_reg(list,size,a,tmpreg);
|
|
a_op_reg_reg_reg_checkoverflow(list,op,size,tmpreg,src,dst,true,ovloc);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation);
|
|
var
|
|
orgsrc1: tregister = NR_NO;
|
|
orgsrc2: tregister = NR_NO;
|
|
docheck: boolean;
|
|
begin
|
|
if not setflags then
|
|
begin
|
|
inherited;
|
|
exit;
|
|
end;
|
|
{ anything else cannot overflow }
|
|
docheck:=size.size in [4,8];
|
|
if docheck then
|
|
begin
|
|
orgsrc1:=src1;
|
|
orgsrc2:=src2;
|
|
if src1=dst then
|
|
begin
|
|
orgsrc1:=getintregister(list,size);
|
|
a_load_reg_reg(list,size,size,src1,orgsrc1);
|
|
end;
|
|
if src2=dst then
|
|
begin
|
|
orgsrc2:=getintregister(list,size);
|
|
a_load_reg_reg(list,size,size,src2,orgsrc2);
|
|
end;
|
|
end;
|
|
a_op_reg_reg_reg(list,op,size,src1,src2,dst);
|
|
if docheck then
|
|
begin
|
|
{ * signed overflow for addition iff
|
|
- src1 and src2 are negative and result is positive (excep in case of
|
|
subtraction, then sign of src1 has to be inverted)
|
|
- src1 and src2 are positive and result is negative
|
|
-> Simplified boolean equivalent (in terms of sign bits):
|
|
not(src1 xor src2) and (src1 xor dst)
|
|
|
|
for subtraction, multiplication: invert src1 sign bit
|
|
for division: handle separately (div by zero, low(inttype) div -1),
|
|
not supported by this code
|
|
|
|
* unsigned overflow iff carry out, aka dst < src1 or dst < src2
|
|
}
|
|
location_reset(ovloc,LOC_REGISTER,OS_S32);
|
|
{ not pasbool8, because then we'd still have to convert the integer to
|
|
a boolean via branches for Dalvik}
|
|
ovloc.register:=getintregister(list,s32inttype);
|
|
if not ((size.typ=pointerdef) or
|
|
((size.typ=orddef) and
|
|
(torddef(size).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
|
|
pasbool1,pasbool8,pasbool16,pasbool32,pasbool64]))) then
|
|
begin
|
|
a_load_reg_stack(list,size,orgsrc1);
|
|
if op in [OP_SUB,OP_IMUL] then
|
|
a_op_stack(list,OP_NOT,size);
|
|
a_op_reg_stack(list,OP_XOR,size,orgsrc2);
|
|
a_op_stack(list,OP_NOT,size);
|
|
a_load_reg_stack(list,size,orgsrc1);
|
|
a_op_reg_stack(list,OP_XOR,size,dst);
|
|
a_op_stack(list,OP_AND,size);
|
|
a_op_const_stack(list,OP_SHR,size,(size.size*8)-1);
|
|
if size.size=8 then
|
|
begin
|
|
//todo: any operands needed?
|
|
list.concat(taicpu.op_none(a_i32_wrap_i64));
|
|
end;
|
|
a_load_stack_reg(list,s32inttype,ovloc.register);
|
|
end
|
|
else
|
|
begin
|
|
if op=OP_SUB then
|
|
begin
|
|
{ unsigned (src1-src2) overflows iff (src1<src2) }
|
|
a_cmp_reg_reg_stack(list,size,OC_B,orgsrc1,orgsrc2);
|
|
a_load_stack_reg(list,s32inttype,ovloc.register);
|
|
end
|
|
else
|
|
begin
|
|
{ can be optimized by removing duplicate xor'ing to convert dst from
|
|
signed to unsigned quadrant }
|
|
a_cmp_reg_reg_stack(list,size,OC_AE,dst,orgsrc1);
|
|
a_cmp_reg_reg_stack(list,size,OC_AE,dst,orgsrc2);
|
|
a_op_stack(list,OP_AND,s32inttype);
|
|
a_load_stack_reg(list,s32inttype,ovloc.register);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
ovloc.loc:=LOC_VOID;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_ref_label(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; const ref: treference; l: tasmlabel);
|
|
begin
|
|
a_cmp_const_ref_stack(list,size,cmp_op,a,ref);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_br_if,l));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_const_reg_label(list: TAsmList; size: tdef; cmp_op: topcmp; a: tcgint; reg: tregister; l: tasmlabel);
|
|
begin
|
|
a_cmp_const_reg_stack(list,size,cmp_op,a,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_br_if,l));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_ref_reg_label(list: TAsmList; size: tdef; cmp_op: topcmp; const ref: treference; reg: tregister; l: tasmlabel);
|
|
begin
|
|
a_cmp_ref_reg_stack(list,size,cmp_op,ref,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_br_if,l));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_ref_label(list: TAsmList; size: tdef; cmp_op: topcmp; reg: tregister; const ref: treference; l: tasmlabel);
|
|
begin
|
|
a_cmp_reg_ref_stack(list,size,cmp_op,reg,ref);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_br_if,l));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_cmp_reg_reg_label(list: TAsmList; size: tdef; cmp_op: topcmp; reg1, reg2: tregister; l: tasmlabel);
|
|
begin
|
|
a_cmp_reg_reg_stack(list,size,cmp_op,reg1,reg2);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_br_if,l));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_jmp_always(list: TAsmList; l: tasmlabel);
|
|
begin
|
|
if (l=current_procinfo.CurrBreakLabel) or
|
|
(l=current_procinfo.CurrContinueLabel) or
|
|
(l=current_procinfo.CurrExitLabel) then
|
|
list.concat(taicpu.op_sym(a_br,l))
|
|
else
|
|
begin
|
|
{$ifndef EXTDEBUG}
|
|
Internalerror(2019091806); // unexpected jump
|
|
{$endif EXTDEBUG}
|
|
list.concat(tai_comment.create(strpnew('Unable to find destination of label '+l.name)));
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_jmp_always_pascal_goto(list: TAsmList; l: tasmlabel);
|
|
var
|
|
br_ins: taicpu;
|
|
begin
|
|
br_ins:=taicpu.op_sym(a_br,l);
|
|
br_ins.is_br_generated_by_goto:=true;
|
|
list.concat(br_ins);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadfpu_ref_ref(list: TAsmList; fromsize, tosize: tdef; const ref1, ref2: treference);
|
|
var
|
|
dstack_slots: longint;
|
|
tmpref1, tmpref2: treference;
|
|
begin
|
|
tmpref1:=ref1;
|
|
tmpref2:=ref2;
|
|
dstack_slots:=prepare_stack_for_ref(list,tmpref2,false);
|
|
a_load_ref_stack(list,fromsize,tmpref1,prepare_stack_for_ref(list,tmpref1,false));
|
|
resizestackfpuval(list,def_cgsize(fromsize),def_cgsize(tosize));
|
|
a_load_stack_ref(list,tosize,tmpref2,dstack_slots);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; reg: tregister);
|
|
var
|
|
tmpref: treference;
|
|
begin
|
|
tmpref:=ref;
|
|
a_load_ref_stack(list,fromsize,tmpref,prepare_stack_for_ref(list,tmpref,false));
|
|
resizestackfpuval(list,def_cgsize(fromsize),def_cgsize(tosize));
|
|
a_load_stack_reg(list,tosize,reg);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tdef; reg: tregister; const ref: treference);
|
|
var
|
|
dstack_slots: longint;
|
|
tmpref: treference;
|
|
begin
|
|
tmpref:=ref;
|
|
dstack_slots:=prepare_stack_for_ref(list,tmpref,false);
|
|
a_load_reg_stack(list,fromsize,reg);
|
|
resizestackfpuval(list,def_cgsize(fromsize),def_cgsize(tosize));
|
|
a_load_stack_ref(list,tosize,tmpref,dstack_slots);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister);
|
|
begin
|
|
a_load_reg_stack(list,fromsize,reg1);
|
|
resizestackfpuval(list,def_cgsize(fromsize),def_cgsize(tosize));
|
|
a_load_stack_reg(list,tosize,reg2);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_unreachable(list: TAsmList);
|
|
begin
|
|
list.Concat(taicpu.op_none(a_unreachable));
|
|
end;
|
|
|
|
procedure thlcgwasm.g_concatcopy(list: TAsmList; size: tdef; const source, dest: treference);
|
|
var
|
|
pd: tprocdef;
|
|
cgpara1,cgpara2,cgpara3 : TCGPara;
|
|
begin
|
|
if (source.base=NR_EVAL_STACK_BASE) or (source.base=NR_LOCAL_STACK_POINTER_REG) or
|
|
(source.index=NR_EVAL_STACK_BASE) or (source.index=NR_LOCAL_STACK_POINTER_REG) or
|
|
(dest.base=NR_EVAL_STACK_BASE) or (dest.base=NR_LOCAL_STACK_POINTER_REG) or
|
|
(dest.index=NR_EVAL_STACK_BASE) or (dest.index=NR_LOCAL_STACK_POINTER_REG) or
|
|
(size.size in [1,2,4,8]) then
|
|
inherited
|
|
else
|
|
begin
|
|
pd:=search_system_proc('MOVE');
|
|
cgpara1.init;
|
|
cgpara2.init;
|
|
cgpara3.init;
|
|
paramanager.getcgtempparaloc(list,pd,1,cgpara1);
|
|
paramanager.getcgtempparaloc(list,pd,2,cgpara2);
|
|
paramanager.getcgtempparaloc(list,pd,3,cgpara3);
|
|
if pd.is_pushleftright then
|
|
begin
|
|
{ load source }
|
|
a_loadaddr_ref_cgpara(list,voidtype,source,cgpara1);
|
|
{ load destination }
|
|
a_loadaddr_ref_cgpara(list,voidtype,dest,cgpara2);
|
|
{ load size }
|
|
a_load_const_cgpara(list,sizesinttype,size.size,cgpara3);
|
|
end
|
|
else
|
|
begin
|
|
{ load size }
|
|
a_load_const_cgpara(list,sizesinttype,size.size,cgpara3);
|
|
{ load destination }
|
|
a_loadaddr_ref_cgpara(list,voidtype,dest,cgpara2);
|
|
{ load source }
|
|
a_loadaddr_ref_cgpara(list,voidtype,source,cgpara1);
|
|
end;
|
|
paramanager.freecgpara(list,cgpara3);
|
|
paramanager.freecgpara(list,cgpara2);
|
|
paramanager.freecgpara(list,cgpara1);
|
|
g_call_system_proc(list,pd,[@cgpara1,@cgpara2,@cgpara3],nil).resetiftemp;
|
|
cgpara3.done;
|
|
cgpara2.done;
|
|
cgpara1.done;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.g_proc_entry(list: TAsmList; localsize: longint; nostackframe: boolean);
|
|
var
|
|
pd: tcpuprocdef;
|
|
begin
|
|
pd:=tcpuprocdef(current_procinfo.procdef);
|
|
g_procdef(list,pd,false);
|
|
|
|
if not nostackframe then
|
|
begin
|
|
|
|
ttgwasm(tg).allocframepointer(list,pd.frame_pointer_ref);
|
|
if pd.base_pointer_ref.base<>NR_LOCAL_STACK_POINTER_REG then
|
|
ttgwasm(tg).allocbasepointer(list,pd.base_pointer_ref);
|
|
|
|
list.Concat(taicpu.op_sym(a_global_get,RefStackPointerSym));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_ref(a_local_set,pd.base_pointer_ref));
|
|
decstack(list,1);
|
|
|
|
if (localsize>0) then begin
|
|
list.Concat(taicpu.op_ref(a_local_get,pd.base_pointer_ref));
|
|
incstack(list,1);
|
|
list.concat(taicpu.op_const(a_i32_const, localsize ));
|
|
incstack(list,1);
|
|
list.concat(taicpu.op_none(a_i32_sub));
|
|
decstack(list,1);
|
|
list.Concat(taicpu.op_ref(a_local_set,pd.frame_pointer_ref));
|
|
decstack(list,1);
|
|
list.Concat(taicpu.op_ref(a_local_get,pd.frame_pointer_ref));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_sym(a_global_set,RefStackPointerSym));
|
|
decstack(list,1);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.g_proc_exit(list: TAsmList; parasize: longint; nostackframe: boolean);
|
|
var
|
|
pd: tcpuprocdef;
|
|
begin
|
|
pd:=tcpuprocdef(current_procinfo.procdef);
|
|
if not nostackframe then
|
|
begin
|
|
list.Concat(taicpu.op_ref(a_local_get,pd.base_pointer_ref));
|
|
incstack(list,1);
|
|
list.Concat(taicpu.op_sym(a_global_set,RefStackPointerSym));
|
|
decstack(list,1);
|
|
end;
|
|
list.concat(taicpu.op_none(a_end_function));
|
|
end;
|
|
|
|
procedure thlcgwasm.g_rangecheck(list: TAsmList; const l: tlocation; fromdef, todef: tdef);
|
|
var
|
|
{$if defined(cpuhighleveltarget)}
|
|
aintmax: tcgint;
|
|
{$elseif defined(cpu64bitalu) or defined(cpu32bitalu)}
|
|
aintmax: aint;
|
|
{$else}
|
|
aintmax: longint;
|
|
{$endif}
|
|
//neglabel : tasmlabel;
|
|
//hreg : tregister;
|
|
lto,hto,
|
|
lfrom,hfrom : TConstExprInt;
|
|
fromsize, tosize: cardinal;
|
|
maxdef: tdef;
|
|
from_signed, to_signed: boolean;
|
|
begin
|
|
{ range checking on and range checkable value? }
|
|
if not(cs_check_range in current_settings.localswitches) or
|
|
not(fromdef.typ in [orddef,enumdef]) or
|
|
{ C-style booleans can't really fail range checks, }
|
|
{ all values are always valid }
|
|
is_cbool(todef) then
|
|
exit;
|
|
{$if not defined(cpuhighleveltarget) and not defined(cpu64bitalu)}
|
|
{ handle 64bit rangechecks separate for 32bit processors }
|
|
if is_64bit(fromdef) or is_64bit(todef) then
|
|
begin
|
|
cg64.g_rangecheck64(list,l,fromdef,todef);
|
|
exit;
|
|
end;
|
|
{$endif ndef cpuhighleveltarget and ndef cpu64bitalu}
|
|
{ only check when assigning to scalar, subranges are different, }
|
|
{ when todef=fromdef then the check is always generated }
|
|
getrange(fromdef,lfrom,hfrom);
|
|
getrange(todef,lto,hto);
|
|
from_signed := is_signed(fromdef);
|
|
to_signed := is_signed(todef);
|
|
{ check the rangedef of the array, not the array itself }
|
|
{ (only change now, since getrange needs the arraydef) }
|
|
if (todef.typ = arraydef) then
|
|
todef := tarraydef(todef).rangedef;
|
|
{ no range check if from and to are equal and are both longint/dword }
|
|
{ (if we have a 32bit processor) or int64/qword, since such }
|
|
{ operations can at most cause overflows (JM) }
|
|
{ Note that these checks are mostly processor independent, they only }
|
|
{ have to be changed once we introduce 64bit subrange types }
|
|
{$if defined(cpuhighleveltarget) or defined(cpu64bitalu)}
|
|
if (fromdef=todef) and
|
|
(fromdef.typ=orddef) and
|
|
(((((torddef(fromdef).ordtype=s64bit) and
|
|
(lfrom = low(int64)) and
|
|
(hfrom = high(int64))) or
|
|
((torddef(fromdef).ordtype=u64bit) and
|
|
(lfrom = low(qword)) and
|
|
(hfrom = high(qword))) or
|
|
((torddef(fromdef).ordtype=scurrency) and
|
|
(lfrom = low(int64)) and
|
|
(hfrom = high(int64)))))) then
|
|
exit;
|
|
{$endif cpuhighleveltarget or cpu64bitalu}
|
|
{ 32 bit operations are automatically widened to 64 bit on 64 bit addr
|
|
targets }
|
|
{$ifdef cpu32bitaddr}
|
|
if (fromdef = todef) and
|
|
(fromdef.typ=orddef) and
|
|
(((((torddef(fromdef).ordtype = s32bit) and
|
|
(lfrom = int64(low(longint))) and
|
|
(hfrom = int64(high(longint)))) or
|
|
((torddef(fromdef).ordtype = u32bit) and
|
|
(lfrom = low(cardinal)) and
|
|
(hfrom = high(cardinal)))))) then
|
|
exit;
|
|
{$endif cpu32bitaddr}
|
|
|
|
{ optimize some range checks away in safe cases }
|
|
fromsize := fromdef.size;
|
|
tosize := todef.size;
|
|
if ((from_signed = to_signed) or
|
|
(not from_signed)) and
|
|
(lto<=lfrom) and (hto>=hfrom) and
|
|
(fromsize <= tosize) then
|
|
begin
|
|
{ if fromsize < tosize, and both have the same signed-ness or }
|
|
{ fromdef is unsigned, then all bit patterns from fromdef are }
|
|
{ valid for todef as well }
|
|
if (fromsize < tosize) then
|
|
exit;
|
|
if (fromsize = tosize) and
|
|
(from_signed = to_signed) then
|
|
{ only optimize away if all bit patterns which fit in fromsize }
|
|
{ are valid for the todef }
|
|
begin
|
|
{$ifopt Q+}
|
|
{$define overflowon}
|
|
{$Q-}
|
|
{$endif}
|
|
{$ifopt R+}
|
|
{$define rangeon}
|
|
{$R-}
|
|
{$endif}
|
|
if to_signed then
|
|
begin
|
|
{ calculation of the low/high ranges must not overflow 64 bit
|
|
otherwise we end up comparing with zero for 64 bit data types on
|
|
64 bit processors }
|
|
if (lto = (int64(-1) << (tosize * 8 - 1))) and
|
|
(hto = (-((int64(-1) << (tosize * 8 - 1))+1))) then
|
|
exit
|
|
end
|
|
else
|
|
begin
|
|
{ calculation of the low/high ranges must not overflow 64 bit
|
|
otherwise we end up having all zeros for 64 bit data types on
|
|
64 bit processors }
|
|
if (lto = 0) and
|
|
(qword(hto) = (qword(-1) >> (64-(tosize * 8))) ) then
|
|
exit
|
|
end;
|
|
{$ifdef overflowon}
|
|
{$Q+}
|
|
{$undef overflowon}
|
|
{$endif}
|
|
{$ifdef rangeon}
|
|
{$R+}
|
|
{$undef rangeon}
|
|
{$endif}
|
|
end
|
|
end;
|
|
|
|
{ depending on the types involved, we perform the range check for 64 or
|
|
for 32 bit }
|
|
if fromsize=8 then
|
|
maxdef:=fromdef
|
|
else
|
|
maxdef:=todef;
|
|
{$if sizeof(aintmax) = 8}
|
|
if maxdef.size=8 then
|
|
aintmax:=high(int64)
|
|
else
|
|
{$endif}
|
|
begin
|
|
aintmax:=high(longint);
|
|
maxdef:=u32inttype;
|
|
end;
|
|
|
|
{ generate the rangecheck code for the def where we are going to }
|
|
{ store the result }
|
|
|
|
{ use the trick that }
|
|
{ a <= x <= b <=> 0 <= x-a <= b-a <=> unsigned(x-a) <= unsigned(b-a) }
|
|
|
|
{ To be able to do that, we have to make sure however that either }
|
|
{ fromdef and todef are both signed or unsigned, or that we leave }
|
|
{ the parts < 0 and > maxlongint out }
|
|
|
|
if from_signed xor to_signed then
|
|
begin
|
|
if from_signed then
|
|
{ from is signed, to is unsigned }
|
|
begin
|
|
{ if high(from) < 0 -> always range error }
|
|
if (hfrom < 0) or
|
|
{ if low(to) > maxlongint also range error }
|
|
(lto > aintmax) then
|
|
begin
|
|
g_call_system_proc(list,'fpc_rangeerror',[],nil).resetiftemp;
|
|
hlcg.g_maybe_checkforexceptions(current_asmdata.CurrAsmList);
|
|
exit
|
|
end;
|
|
{ from is signed and to is unsigned -> when looking at to }
|
|
{ as an signed value, it must be < maxaint (otherwise }
|
|
{ it will become negative, which is invalid since "to" is unsigned) }
|
|
if hto > aintmax then
|
|
hto := aintmax;
|
|
end
|
|
else
|
|
{ from is unsigned, to is signed }
|
|
begin
|
|
if (lfrom > aintmax) or
|
|
(hto < 0) then
|
|
begin
|
|
g_call_system_proc(list,'fpc_rangeerror',[],nil).resetiftemp;
|
|
hlcg.g_maybe_checkforexceptions(current_asmdata.CurrAsmList);
|
|
exit
|
|
end;
|
|
{ from is unsigned and to is signed -> when looking at to }
|
|
{ as an unsigned value, it must be >= 0 (since negative }
|
|
{ values are the same as values > maxlongint) }
|
|
if lto < 0 then
|
|
lto := 0;
|
|
end;
|
|
end;
|
|
a_load_loc_stack(list,fromdef,l);
|
|
resize_stack_int_val(list,fromdef,maxdef,false);
|
|
a_load_const_stack(list,maxdef,tcgint(int64(lto)),R_INTREGISTER);
|
|
a_op_stack(list,OP_SUB,maxdef);
|
|
{
|
|
if from_signed then
|
|
a_cmp_const_reg_label(list,OS_INT,OC_GTE,aint(hto-lto),hreg,neglabel)
|
|
else
|
|
}
|
|
if qword(hto-lto)>qword(aintmax) then
|
|
a_load_const_stack(list,maxdef,aintmax,R_INTREGISTER)
|
|
else
|
|
a_load_const_stack(list,maxdef,tcgint(int64(hto-lto)),R_INTREGISTER);
|
|
|
|
a_cmp_stack_stack(list,maxdef,OC_A);
|
|
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
|
|
thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
|
|
|
g_call_system_proc(list,'fpc_rangeerror',[],nil).resetiftemp;
|
|
hlcg.g_maybe_checkforexceptions(current_asmdata.CurrAsmList);
|
|
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
|
|
end;
|
|
|
|
procedure thlcgwasm.g_overflowcheck(list: TAsmList; const Loc: tlocation; def: tdef);
|
|
begin
|
|
{ not possible, need the original operands }
|
|
internalerror(2012102101);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_overflowCheck_loc(List: TAsmList; const Loc: TLocation; def: TDef; var ovloc: tlocation);
|
|
var
|
|
hl : tasmlabel;
|
|
begin
|
|
if not(cs_check_overflow in current_settings.localswitches) then
|
|
exit;
|
|
current_asmdata.getjumplabel(hl);
|
|
list.concat(taicpu.op_none(a_block));
|
|
a_cmp_const_loc_label(list,s32inttype,OC_EQ,0,ovloc,hl);
|
|
g_call_system_proc(list,'fpc_overflow',[],nil);
|
|
hlcg.g_maybe_checkforexceptions(current_asmdata.CurrAsmList);
|
|
list.concat(taicpu.op_none(a_end_block));
|
|
a_label(list,hl);
|
|
end;
|
|
|
|
procedure thlcgwasm.maybe_change_load_node_reg(list: TAsmList; var n: tnode; reload: boolean);
|
|
begin
|
|
{ don't do anything, all registers become stack locations anyway }
|
|
end;
|
|
|
|
procedure thlcgwasm.gen_entry_code(list: TAsmList);
|
|
begin
|
|
inherited;
|
|
if not (po_assembler in current_procinfo.procdef.procoptions) then
|
|
begin
|
|
list.concat(taicpu.op_none(a_block));
|
|
list.concat(taicpu.op_none(a_block));
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.gen_exit_code(list: TAsmList);
|
|
begin
|
|
if not (po_assembler in current_procinfo.procdef.procoptions) then
|
|
begin
|
|
list.concat(taicpu.op_none(a_end_block));
|
|
if ts_wasm_bf_exceptions in current_settings.targetswitches then
|
|
a_label(list,tcpuprocinfo(current_procinfo).CurrRaiseLabel);
|
|
if fevalstackheight<>0 then
|
|
{$ifdef DEBUG_WASMSTACK}
|
|
list.concat(tai_comment.Create(strpnew('!!! values remaining on stack at end of block !!!')));
|
|
{$else DEBUG_WASMSTACK}
|
|
internalerror(2021091801);
|
|
{$endif DEBUG_WASMSTACK}
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
procedure thlcgwasm.gen_stack_check_size_para(list: TAsmList);
|
|
begin
|
|
{ HACK: this is called *after* gen_stack_check_call, but the code it
|
|
generates is inserted *before* the call. Thus, it breaks our
|
|
incstack/decstack tracking and causes an internal error 2010120501. We
|
|
workaround this by generating a const instruction without calling
|
|
incstack, and instead we call incstack before the call, in
|
|
gen_stack_check_call. }
|
|
list.concat(taicpu.op_const(a_i32_const,current_procinfo.calc_stackframe_size));
|
|
end;
|
|
|
|
procedure thlcgwasm.gen_stack_check_call(list: TAsmList);
|
|
begin
|
|
{ HACK: see the comment in gen_stack_check_size_para }
|
|
incstack(list,1);
|
|
inherited;
|
|
end;
|
|
|
|
procedure thlcgwasm.a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tdef; src, dst: tregister);
|
|
begin
|
|
internalerror(2012090201);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadmm_loc_reg(list: TAsmList; fromsize, tosize: tdef; const loc: tlocation; const reg: tregister; shuffle: pmmshuffle);
|
|
begin
|
|
internalerror(2012090202);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadmm_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister; shuffle: pmmshuffle);
|
|
begin
|
|
internalerror(2012060130);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadmm_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; reg: tregister; shuffle: pmmshuffle);
|
|
begin
|
|
internalerror(2012060131);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadmm_reg_ref(list: TAsmList; fromsize, tosize: tdef; reg: tregister; const ref: treference; shuffle: pmmshuffle);
|
|
begin
|
|
internalerror(2012060132);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_opmm_reg_reg(list: TAsmList; Op: TOpCG; size: tdef; src, dst: tregister; shuffle: pmmshuffle);
|
|
begin
|
|
internalerror(2012060133);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadmm_intreg_reg(list: TAsmList; fromsize, tosize: tdef; intreg, mmreg: tregister; shuffle: pmmshuffle);
|
|
begin
|
|
internalerror(2012060134);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_loadmm_reg_intreg(list: TAsmList; fromsize, tosize: tdef; mmreg, intreg: tregister; shuffle: pmmshuffle);
|
|
begin
|
|
internalerror(2012060135);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_stackpointer_alloc(list: TAsmList; size: longint);
|
|
begin
|
|
internalerror(2012090203);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);
|
|
begin
|
|
internalerror(2012090204);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_adjust_self_value(list: TAsmList; procdef: tprocdef; ioffset: aint);
|
|
begin
|
|
internalerror(2012090205);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_local_unwind(list: TAsmList; l: TAsmLabel);
|
|
begin
|
|
internalerror(2012090206);
|
|
end;
|
|
|
|
procedure thlcgwasm.g_procdef(list: TAsmList; pd: tprocdef; is_forward: Boolean);
|
|
begin
|
|
if not pd.is_generic then
|
|
list.Concat(tai_functype.create(pd.mangledname,tcpuprocdef(pd).create_functype,is_forward));
|
|
end;
|
|
|
|
procedure thlcgwasm.g_maybe_checkforexceptions(list: TasmList);
|
|
var
|
|
pd: tprocdef;
|
|
begin
|
|
if ts_wasm_bf_exceptions in current_settings.targetswitches then
|
|
begin
|
|
pd:=search_system_proc('fpc_raised_exception_flag');
|
|
g_call_system_proc(list,pd,[],nil).resetiftemp;
|
|
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
|
|
list.concat(taicpu.op_sym(a_br_if,tcpuprocinfo(current_procinfo).CurrRaiseLabel));
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.g_load_check_simple(list: TAsmList; const ref: treference; size: aint);
|
|
var
|
|
reg: tregister;
|
|
begin
|
|
if not(cs_check_low_addr_load in current_settings.localswitches) then
|
|
exit;
|
|
{ A global symbol (if not weak) will always map to a proper address, and
|
|
the same goes for stack addresses -> skip }
|
|
if assigned(ref.symbol) and
|
|
(ref.symbol.bind<>AB_WEAK_EXTERNAL) then
|
|
exit;
|
|
if (ref.base=NR_STACK_POINTER_REG) or
|
|
(ref.index=NR_STACK_POINTER_REG) or
|
|
(ref.base=NR_EVAL_STACK_BASE) or
|
|
(ref.index=NR_EVAL_STACK_BASE) or
|
|
(assigned(current_procinfo) and
|
|
((ref.base=current_procinfo.framepointer) or
|
|
(ref.index=current_procinfo.framepointer))) then
|
|
exit;
|
|
if assigned(ref.symbol) or
|
|
(ref.offset<>0) or
|
|
((ref.base<>NR_NO) and (ref.index<>NR_NO)) or
|
|
((ref.base=NR_NO) and (ref.index=NR_NO)) then
|
|
begin
|
|
reg:=getintregister(list,voidpointertype);
|
|
a_loadaddr_ref_reg(list,voidpointertype,voidpointertype,ref,reg);
|
|
end
|
|
else if ref.base<>NR_NO then
|
|
reg:=ref.base
|
|
else
|
|
reg:=ref.index;
|
|
a_cmp_const_reg_stack(list,voidpointertype,OC_B,size,reg);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
|
|
decstack(current_asmdata.CurrAsmList,1);
|
|
g_call_system_proc(list,'fpc_invalidpointer',[],nil);
|
|
hlcg.g_maybe_checkforexceptions(current_asmdata.CurrAsmList);
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_stack_reg(list: TAsmList; size: tdef; reg: tregister);
|
|
begin
|
|
list.concat(taicpu.op_reg(a_local_set,reg));
|
|
decstack(list,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_stack_ref(list: TAsmList; size: tdef; const ref: treference; extra_slots: longint);
|
|
var
|
|
opc: tasmop;
|
|
finishandval: tcgint;
|
|
begin
|
|
{ fake location that indicates the value has to remain on the stack }
|
|
if ref.base=NR_EVAL_STACK_BASE then
|
|
exit;
|
|
opc:=loadstoreopcref(size,false,ref,finishandval);
|
|
|
|
if ref.refaddr=addr_got_tls then
|
|
list.concat(taicpu.op_const(opc,ref.offset))
|
|
else
|
|
list.concat(taicpu.op_ref(opc,ref));
|
|
{ avoid problems with getting the size of an open array etc }
|
|
if wasmAlwayInMem(size) then
|
|
size:=ptruinttype;
|
|
decstack(list,1+extra_slots);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_reg_stack(list: TAsmList; size: tdef; reg: tregister);
|
|
begin
|
|
list.concat(taicpu.op_reg(a_local_get,reg));
|
|
incstack(list,1);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_ref_stack(list: TAsmList; size: tdef; const ref: treference; extra_slots: longint);
|
|
var
|
|
opc: tasmop;
|
|
finishandval: tcgint;
|
|
begin
|
|
{ fake location that indicates the value is already on the stack? }
|
|
if (ref.base=NR_EVAL_STACK_BASE) then
|
|
exit;
|
|
opc:=loadstoreopcref(size,true,ref,finishandval);
|
|
|
|
if ref.refaddr=addr_got_tls then
|
|
list.concat(taicpu.op_const(opc,ref.offset))
|
|
else
|
|
list.concat(taicpu.op_ref(opc,ref));
|
|
|
|
{ avoid problems with getting the size of an open array etc }
|
|
if wasmAlwayInMem(size) then
|
|
size:=ptruinttype;
|
|
incstack(list,1-extra_slots);
|
|
if finishandval<>-1 then
|
|
a_op_const_stack(list,OP_AND,size,finishandval);
|
|
|
|
// there's no cast check in Wasm
|
|
//if ref.checkcast then
|
|
// gen_typecheck(list,a_checkcast,size);
|
|
end;
|
|
|
|
procedure thlcgwasm.a_load_subsetref_stack(list : TAsmList;size: tdef; const sref: tsubsetreference);
|
|
var
|
|
tmpreg: TRegister;
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_subsetref_reg(list,size,size,sref,tmpreg);
|
|
a_load_reg_stack(list,size,tmpreg);
|
|
end;
|
|
|
|
function thlcgwasm.loadstoreopcref(def: tdef; isload: boolean; const ref: treference; out finishandval: tcgint): tasmop;
|
|
const
|
|
{iisload} {issigned}
|
|
getputmem8 : array [boolean, boolean] of TAsmOp = ((a_i32_store8, a_i32_store8), (a_i32_load8_u, a_i32_load8_s));
|
|
getputmem16 : array [boolean, boolean] of TAsmOp = ((a_i32_store16, a_i32_store16), (a_i32_load16_u ,a_i32_load16_s));
|
|
getputmem32 : array [boolean, boolean] of TAsmOp = ((a_i32_store, a_i32_store), (a_i32_load, a_i32_load));
|
|
getputmem64 : array [boolean, boolean] of TAsmOp = ((a_i64_store, a_i64_store), (a_i64_load, a_i64_load));
|
|
getputmemf32 : array [boolean] of TAsmOp = (a_f32_store, a_f32_load);
|
|
getputmemf64 : array [boolean] of TAsmOp = (a_f64_store, a_f64_load);
|
|
begin
|
|
if assigned(ref.symbol) and (ref.symbol.typ=AT_WASM_GLOBAL) then
|
|
begin
|
|
if isload then
|
|
result:=a_global_get
|
|
else
|
|
result:=a_global_set;
|
|
finishandval:=-1;
|
|
end
|
|
else if (ref.base<>NR_LOCAL_STACK_POINTER_REG) or assigned(ref.symbol) then
|
|
begin
|
|
{ -> either a global (static) field, or a regular field. If a regular
|
|
field, then ref.base contains the self pointer, otherwise
|
|
ref.base=NR_NO. In both cases, the symbol contains all other
|
|
information (combined field name and type descriptor) }
|
|
case def.size of
|
|
1: result := getputmem8[isload, is_signed(def)];
|
|
2: result := getputmem16[isload, is_signed(def)];
|
|
4:
|
|
if is_single(def) or ((def.typ=recorddef) and (trecorddef(def).contains_float_field)) then
|
|
result := getputmemf32[isload]
|
|
else
|
|
result := getputmem32[isload, is_signed(def)];
|
|
8: if is_double(def) or ((def.typ=recorddef) and (trecorddef(def).contains_float_field)) then
|
|
result := getputmemf64[isload]
|
|
else
|
|
result := getputmem64[isload, is_signed(def)];
|
|
else
|
|
Internalerror(2019091501);
|
|
end;
|
|
|
|
//result:=getputopc[isload,ref.base=NR_NO];
|
|
finishandval:=-1;
|
|
{ erase sign extension for byte/smallint loads }
|
|
if (def2regtyp(def)=R_INTREGISTER) and
|
|
not is_signed(def) and
|
|
(def.typ=orddef) and
|
|
not is_widechar(def) then
|
|
case def.size of
|
|
1: if (torddef(def).high>127) then
|
|
finishandval:=255;
|
|
2: if (torddef(def).high>32767) then
|
|
finishandval:=65535;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
finishandval:=-1;
|
|
if isload then
|
|
result := a_local_get
|
|
else
|
|
result := a_local_set;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.resize_stack_int_val(list: TAsmList; fromsize, tosize: tdef; formemstore: boolean);
|
|
var
|
|
fromcgsize, tocgsize: tcgsize;
|
|
begin
|
|
{ When storing to an array, field or global variable, make sure the
|
|
static type verification can determine that the stored value fits
|
|
within the boundaries of the declared type (to appease the Dalvik VM).
|
|
Local variables either get their type upgraded in the debug info,
|
|
or have no type information at all }
|
|
if formemstore and
|
|
(tosize.typ=orddef) then
|
|
if (torddef(tosize).ordtype in [u8bit,uchar]) then
|
|
tosize:=s8inttype
|
|
else if torddef(tosize).ordtype=u16bit then
|
|
tosize:=s16inttype;
|
|
|
|
fromcgsize:=def_cgsize(fromsize);
|
|
tocgsize:=def_cgsize(tosize);
|
|
if fromcgsize in [OS_S64,OS_64] then
|
|
begin
|
|
if not(tocgsize in [OS_S64,OS_64]) then
|
|
begin
|
|
{ truncate }
|
|
list.concat(taicpu.op_none(a_i32_wrap_i64));
|
|
case tocgsize of
|
|
OS_8:
|
|
a_op_const_stack(list,OP_AND,s32inttype,255);
|
|
OS_S8:
|
|
list.concat(taicpu.op_none(a_i32_extend8_s));
|
|
OS_16:
|
|
a_op_const_stack(list,OP_AND,s32inttype,65535);
|
|
OS_S16:
|
|
list.concat(taicpu.op_none(a_i32_extend16_s));
|
|
OS_32,OS_S32:
|
|
;
|
|
else
|
|
internalerror(2021012201);
|
|
end;
|
|
end;
|
|
end
|
|
else if tocgsize in [OS_S64,OS_64] then
|
|
begin
|
|
{ extend }
|
|
case fromcgsize of
|
|
OS_8:
|
|
begin
|
|
a_op_const_stack(list,OP_AND,s32inttype,255);
|
|
list.concat(taicpu.op_none(a_i64_extend_i32_u));
|
|
end;
|
|
OS_S8:
|
|
begin
|
|
list.concat(taicpu.op_none(a_i64_extend_i32_u));
|
|
list.concat(taicpu.op_none(a_i64_extend8_s));
|
|
end;
|
|
OS_16:
|
|
begin
|
|
a_op_const_stack(list,OP_AND,s32inttype,65535);
|
|
list.concat(taicpu.op_none(a_i64_extend_i32_u));
|
|
end;
|
|
OS_S16:
|
|
begin
|
|
list.concat(taicpu.op_none(a_i64_extend_i32_u));
|
|
list.concat(taicpu.op_none(a_i64_extend16_s));
|
|
end;
|
|
OS_32:
|
|
list.concat(taicpu.op_none(a_i64_extend_i32_u));
|
|
OS_S32:
|
|
list.concat(taicpu.op_none(a_i64_extend_i32_s));
|
|
OS_64,OS_S64:
|
|
;
|
|
else
|
|
internalerror(2021010301);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if tcgsize2size[fromcgsize]<tcgsize2size[tocgsize] then
|
|
begin
|
|
{ extend }
|
|
case fromcgsize of
|
|
OS_8:
|
|
a_op_const_stack(list,OP_AND,s32inttype,255);
|
|
OS_S8:
|
|
begin
|
|
list.concat(taicpu.op_none(a_i32_extend8_s));
|
|
if tocgsize=OS_16 then
|
|
a_op_const_stack(list,OP_AND,s32inttype,65535);
|
|
end;
|
|
OS_16:
|
|
a_op_const_stack(list,OP_AND,s32inttype,65535);
|
|
OS_S16:
|
|
list.concat(taicpu.op_none(a_i32_extend16_s));
|
|
OS_32,OS_S32:
|
|
;
|
|
else
|
|
internalerror(2021010302);
|
|
end;
|
|
end
|
|
else if tcgsize2size[fromcgsize]>=tcgsize2size[tocgsize] then
|
|
begin
|
|
{ truncate }
|
|
case tocgsize of
|
|
OS_8:
|
|
a_op_const_stack(list,OP_AND,s32inttype,255);
|
|
OS_S8:
|
|
list.concat(taicpu.op_none(a_i32_extend8_s));
|
|
OS_16:
|
|
a_op_const_stack(list,OP_AND,s32inttype,65535);
|
|
OS_S16:
|
|
list.concat(taicpu.op_none(a_i32_extend16_s));
|
|
OS_32,OS_S32:
|
|
;
|
|
else
|
|
internalerror(2021010302);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure thlcgwasm.maybe_resize_stack_para_val(list: TAsmList; retdef: tdef; callside: boolean);
|
|
var
|
|
convsize: tdef;
|
|
begin
|
|
if (retdef.typ=orddef) then
|
|
begin
|
|
if (torddef(retdef).ordtype in [u8bit,u16bit,uchar]) and
|
|
(torddef(retdef).high>=(1 shl (retdef.size*8-1))) then
|
|
begin
|
|
convsize:=nil;
|
|
if callside then
|
|
if torddef(retdef).ordtype in [u8bit,uchar] then
|
|
convsize:=s8inttype
|
|
else
|
|
convsize:=s16inttype
|
|
else if torddef(retdef).ordtype in [u8bit,uchar] then
|
|
convsize:=u8inttype
|
|
else
|
|
convsize:=u16inttype;
|
|
if assigned(convsize) then
|
|
resize_stack_int_val(list,s32inttype,convsize,false);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure thlcgwasm.g_adjust_stack_after_call(list: TAsmList; pd: tabstractprocdef);
|
|
var
|
|
totalremovesize: longint;
|
|
realresdef: tdef;
|
|
ft: TWasmFuncType;
|
|
begin
|
|
if pd.typ=procvardef then
|
|
ft:=tcpuprocvardef(pd).create_functype
|
|
else
|
|
ft:=tcpuprocdef(pd).create_functype;
|
|
totalremovesize:=Length(ft.params)-Length(ft.results);
|
|
{ remove parameters from internal evaluation stack counter (in case of
|
|
e.g. no parameters and a result, it can also increase) }
|
|
if totalremovesize>0 then
|
|
decstack(list,totalremovesize)
|
|
else if totalremovesize<0 then
|
|
incstack(list,-totalremovesize);
|
|
ft.free;
|
|
end;
|
|
|
|
|
|
procedure thlcgwasm.resizestackfpuval(list: TAsmList; fromsize, tosize: tcgsize);
|
|
begin
|
|
if (fromsize=OS_F32) and
|
|
(tosize=OS_F64) then
|
|
begin
|
|
list.concat(taicpu.op_none(a_f64_promote_f32));
|
|
end
|
|
else if (fromsize=OS_F64) and
|
|
(tosize=OS_F32) then
|
|
begin
|
|
list.concat(taicpu.op_none(a_f32_demote_f64));
|
|
end;
|
|
end;
|
|
|
|
procedure create_hlcodegen_cpu;
|
|
begin
|
|
hlcg:=thlcgwasm.create;
|
|
create_codegen;
|
|
end;
|
|
|
|
initialization
|
|
chlcgobj:=thlcgwasm;
|
|
create_hlcodegen:=@create_hlcodegen_cpu;
|
|
end.
|