tcginnode.pass_generate_code: use tlhcgobj.g_undefined_ok

Also override the code that uses this for LLVM with a variant that does not
need it for LLVM versions that do not support the freeze instruction.
This commit is contained in:
Jonas Maebe 2023-01-01 12:11:30 +01:00
parent ab581c5c30
commit 29bae2297f
2 changed files with 86 additions and 24 deletions

View File

@ -26,9 +26,16 @@ unit nllvmset;
interface
uses
nset, ncgset;
nset, ncgset,
symtype,
cgbase;
type
tllvminnode = class(tcginnode)
protected
procedure in_reg_const(uopdef: tdef; opsize: tcgsize); override;
end;
tllvmcasenode = class(tcgcasenode)
protected
procedure genlinearlist(hp: pcaselabel); override;
@ -37,6 +44,42 @@ interface
implementation
uses
globals,
aasmbase, aasmdata,
hlcgobj,
llvminfo;
procedure tllvminnode.in_reg_const(uopdef: tdef; opsize: tcgsize);
var
hl,hlend: TAsmLabel;
begin
if not(llvmflag_no_freeze in llvmversion_properties[current_settings.llvmversion]) then
begin
{ if we have the freeze instruction, we can mark the poison value
potentially generated by the shift as "replace with a fixed value
if it's poison, we don't care since we'll mask it anyway" }
inherited;
exit;
end;
{ don't perform the bit test if the register's value can be >=
"number of bits in right.location", because that generates a poison
value in LLVM (and even anding it will 0 will keep it a poison value),
and calculations using poison as input result in undefined behaviour }
current_asmdata.getjumplabel(hl);
current_asmdata.getjumplabel(hlend);
hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,uopdef,OC_AE,
right.resultdef.packedbitsize,left.location.register,hl);
hlcg.a_bit_test_reg_loc_reg(current_asmdata.CurrAsmList,
uopdef,right.resultdef,uopdef,
left.location.register,right.location,location.register);
hlcg.a_jmp_always(current_asmdata.CurrAsmList,hlend);
hlcg.a_label(current_asmdata.CurrAsmList,hl);
hlcg.a_load_const_reg(current_asmdata.CurrAsmList,uopdef,0,location.register);
hlcg.a_label(current_asmdata.CurrAsmList,hlend);
end;
procedure tllvmcasenode.genlinearlist(hp: pcaselabel);
begin
{ genlinearlist constantly updates the case value in the register,
@ -48,6 +91,7 @@ implementation
end;
begin
cinnode:=tllvminnode;
ccasenode:=tllvmcasenode;
end.

View File

@ -51,6 +51,7 @@ interface
protected
function checkgenjumps(out setparts: Tsetparts; out numparts: byte; out use_small: boolean): boolean; virtual;
function analizeset(const Aset:Tconstset;out setparts: Tsetparts; out numparts: byte;is_small:boolean):boolean;virtual;
procedure in_reg_const(uopdef: tdef; opsize: tcgsize);virtual;
end;
tcgcasenode = class(tcasenode)
@ -183,6 +184,45 @@ implementation
end;
procedure tcginnode.in_reg_const(uopdef: tdef; opsize: tcgsize);
var
hr: tregister;
begin
{ emit bit test operation -- warning: do not use
location_force_reg() to force a set into a register, except
to a register of the same size as the set. The reason is
that on big endian systems, this would require moving the
set to the most significant part of the new register,
and location_force_register can't do that (it does not
know the type).
a_bit_test_reg_loc_reg() properly takes into account the
size of the set to adjust the register index to test }
hlcg.a_bit_test_reg_loc_reg(current_asmdata.CurrAsmList,
uopdef, right.resultdef, uopdef,
left.location.register, right.location, location.register);
{ this may have resulted in an undefined value if left.location.register
was >= size of right.location, but we'll mask the value to zero below
in that case -> okay }
hlcg.g_undefined_ok(current_asmdata.CurrAsmList,uopdef,location.register);
{ now zero the result if left > nr_of_bits_in_right_register }
hr := hlcg.getintregister(current_asmdata.CurrAsmList, uopdef);
{ if left > tcgsize2size[opsize]*8 then hr := 0 else hr := $ffffffff }
{ (left.location.size = location.size at this point) }
hlcg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SUB, uopdef,
tcgsize2size[opsize]*8, left.location.register, hr);
hlcg.a_op_const_reg(current_asmdata.CurrAsmList, OP_SAR, uopdef, (tcgsize2size
[opsize]*8)-1, hr);
{ if left > tcgsize2size[opsize]*8-1, then result := 0 else result := result of bit test }
hlcg.a_op_reg_reg(current_asmdata.CurrAsmList, OP_AND, uopdef, hr,
location.register);
end;
procedure tcginnode.in_smallset(opdef: tdef; setbase: aint);
begin
{ location is always LOC_REGISTER }
@ -445,29 +485,7 @@ implementation
{ load left in register }
hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,uopdef,true);
register_maybe_adjust_setbase(current_asmdata.CurrAsmList,uopdef,left.location,setbase);
{ emit bit test operation -- warning: do not use
location_force_reg() to force a set into a register, except
to a register of the same size as the set. The reason is
that on big endian systems, this would require moving the
set to the most significant part of the new register,
and location_force_register can't do that (it does not
know the type).
a_bit_test_reg_loc_reg() properly takes into account the
size of the set to adjust the register index to test }
hlcg.a_bit_test_reg_loc_reg(current_asmdata.CurrAsmList,
uopdef,right.resultdef,uopdef,
left.location.register,right.location,location.register);
{ now zero the result if left > nr_of_bits_in_right_register }
hr := hlcg.getintregister(current_asmdata.CurrAsmList,uopdef);
{ if left > tcgsize2size[opsize]*8 then hr := 0 else hr := $ffffffff }
{ (left.location.size = location.size at this point) }
hlcg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SUB, uopdef, tcgsize2size[opsize]*8, left.location.register, hr);
hlcg.a_op_const_reg(current_asmdata.CurrAsmList, OP_SAR, uopdef, (tcgsize2size[opsize]*8)-1, hr);
{ if left > tcgsize2size[opsize]*8-1, then result := 0 else result := result of bit test }
hlcg.a_op_reg_reg(current_asmdata.CurrAsmList, OP_AND, uopdef, hr, location.register);
in_reg_const(uopdef, opsize);
end { of right.location.loc=LOC_CONSTANT }
{ do search in a normal set which could have >32 elements
but also used if the left side contains higher values > 32 }