diff --git a/compiler/wasm32/hlcgcpu.pas b/compiler/wasm32/hlcgcpu.pas index d8d58a276a..82b0af717f 100644 --- a/compiler/wasm32/hlcgcpu.pas +++ b/compiler/wasm32/hlcgcpu.pas @@ -73,6 +73,7 @@ uses procedure a_load_ref_ref(list : TAsmList;fromsize, tosize : tdef;const sref : treference;const dref : 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; @@ -1215,6 +1216,244 @@ implementation 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*80) 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)); + incblock; + 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)); + decblock; + 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);