diff --git a/compiler/options.pas b/compiler/options.pas index 39c7f098ab..35aff5c7cf 100644 --- a/compiler/options.pas +++ b/compiler/options.pas @@ -3850,7 +3850,7 @@ if (target_info.abi = abi_eabihf) then {$endif ARM} { inline bsf/bsr implementation } -{$if defined(i386) or defined(x86_64) or defined(aarch64)} +{$if defined(i386) or defined(x86_64) or defined(aarch64) or defined(powerpc) or defined(powerpc64)} def_system_macro('FPC_HAS_INTERNAL_BSF'); def_system_macro('FPC_HAS_INTERNAL_BSR'); {$endif} diff --git a/compiler/ppcgen/cgppc.pas b/compiler/ppcgen/cgppc.pas index 5a5c11be3c..dc5fae68ab 100644 --- a/compiler/ppcgen/cgppc.pas +++ b/compiler/ppcgen/cgppc.pas @@ -35,6 +35,8 @@ unit cgppc; tcgppcgen = class(tcg) procedure a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : tcgpara); override; + procedure a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister); override; + procedure a_call_reg(list : TAsmList;reg: tregister); override; { stores the contents of register reg to the memory location described by @@ -216,6 +218,54 @@ unit cgppc; end; + procedure tcgppcgen.a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister); + var + tmpreg: tregister; + cntlzop: tasmop; + bitsizem1: longint; + begin + { we only have a cntlz(w|d) instruction, which corresponds to bsr(x) + (well, regsize_in_bits - bsr(x), as x86 numbers bits in reverse). + Fortunately, bsf(x) can be calculated easily based on that, see + "Figure 5-13. Number of Powers of 2 Code Sequence" in the PowerPC + Compiler Writer's Guide + } + if srcsize in [OS_64,OS_S64] then + begin +{$ifdef powerpc64} + cntlzop:=A_CNTLZD; +{$else} + internalerror(2015022601); +{$endif} + bitsizem1:=63; + end + else + begin + cntlzop:=A_CNTLZW; + bitsizem1:=31; + end; + if not reverse then + begin + { cntlzw(src and -src) } + tmpreg:=getintregister(list,srcsize); + { don't use a_op_reg_reg, as this will adjust the result + after the neg in case of a non-32/64 bit operation, which + is not necessary since we're only using it as an + AND-mask } + list.concat(taicpu.op_reg_reg(A_NEG,tmpreg,src)); + a_op_reg_reg(list,OP_AND,srcsize,src,tmpreg); + end + else + tmpreg:=src; + { count leading zeroes } + list.concat(taicpu.op_reg_reg(cntlzop,dst,tmpreg)); + { (bitsize-1) - cntlz (which is 32/64 in case src was 0) } + list.concat(taicpu.op_reg_reg_const(A_SUBFIC,dst,dst,bitsizem1)); + { set to 255 is source was 0 } + a_op_const_reg(list,OP_AND,dstsize,255,dst); + end; + + procedure tcgppcgen.g_maybe_got_init(list: TAsmList); var instr: taicpu; diff --git a/rtl/inc/systemh.inc b/rtl/inc/systemh.inc index 35ba88b9b4..2b94aa618f 100644 --- a/rtl/inc/systemh.inc +++ b/rtl/inc/systemh.inc @@ -987,23 +987,23 @@ function fpc_SarInt64(Const AValue : Int64;const Shift : Byte): Int64;compilerpr {$endif FPC_HAS_INTERNAL_SAR_QWORD} {$ifdef FPC_HAS_INTERNAL_BSF} -{$if defined(cpui386) or defined(cpux86_64) or defined(cpuarm) or defined(cpuaarch64)} +{$if defined(cpui386) or defined(cpux86_64) or defined(cpuarm) or defined(cpuaarch64) or defined(cpupowerpc32) or defined(cpupowerpc64)} {$define FPC_HAS_INTERNAL_BSF_BYTE} {$define FPC_HAS_INTERNAL_BSF_WORD} {$define FPC_HAS_INTERNAL_BSF_DWORD} {$endif} -{$if defined(cpux86_64) or defined(cpuaarch64)} +{$if defined(cpux86_64) or defined(cpuaarch64) or defined(cpupowerpc64)} {$define FPC_HAS_INTERNAL_BSF_QWORD} {$endif} {$endif} {$ifdef FPC_HAS_INTERNAL_BSR} -{$if defined(cpui386) or defined(cpux86_64) or defined(cpuarm) or defined(cpuaarch64)} +{$if defined(cpui386) or defined(cpux86_64) or defined(cpuarm) or defined(cpuaarch64) or defined(cpupowerpc32) or defined(cpupowerpc64)} {$define FPC_HAS_INTERNAL_BSR_BYTE} {$define FPC_HAS_INTERNAL_BSR_WORD} {$define FPC_HAS_INTERNAL_BSR_DWORD} {$endif} -{$if defined(cpux86_64) or defined(cpuaarch64)} +{$if defined(cpux86_64) or defined(cpuaarch64) or defined(cpupowerpc64)} {$define FPC_HAS_INTERNAL_BSR_QWORD} {$endif} {$endif}