[DTrace-devel] [PATCH 1/4] clean up dt_cg_typecast(): unnecessary move from register to itself

Kris Van Hees kris.van.hees at oracle.com
Tue Aug 11 17:48:27 PDT 2020


On Tue, Aug 11, 2020 at 06:11:09PM -0400, eugene.loh at oracle.com wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
> 
> The unnecessary move is here:
>     1147    instr = BPF_MOV_REG(dst->dn_reg, src->dn_reg);
>     1148    dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
> 
> This move is unnecessary because dt_cg_typeset()'s callers always
> set dst->dn_reg=src->dn_reg:
> 
>     $ grep -B 1 dt_cg_typecast dtrace-user/libdtrace/dt_cg.c
>     static void
>     dt_cg_typecast(const dt_node_t *src, const dt_node_t *dst,
>     --
>         isp->dis_args[i].dn_reg = dnp->dn_reg; /* re-use register */
>         dt_cg_typecast(dnp, &isp->dis_args[i], dlp, drp);
>     --
>         mnp->dn_reg = mnp->dn_membexpr->dn_reg;
>         dt_cg_typecast(mnp->dn_membexpr, mnp, dlp, drp);
>     --
>         dnp->dn_reg = inp->din_root->dn_reg;
>         dt_cg_typecast(inp->din_root, dnp, dlp, drp);
>     --
>         dnp->dn_reg = dnp->dn_right->dn_reg;
>         dt_cg_typecast(dnp->dn_right, dnp, dlp, drp);
>     --
>         dnp->dn_reg = mnp->dn_membexpr->dn_reg;
>         dt_cg_typecast(mnp->dn_membexpr, dnp, dlp, drp);
> 
> Indeed, dt_cg_typecast() expects src and dst will already be in
> the same register.  For example, there is no move from src->dn_reg
> to dst->dn_reg if no extra typecast is needed.  And historically,
> that register move never existed;  it was introduced in the port
> to BPF:
> 
>     % git show ad8e9312f5c2
>     [...]
>     Convert the D compiler to generate BPF
>     [...]
>     diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
>     [...]
>     @@ -464,12 +482,13 @@ dt_cg_typecast(const dt_node_t *src, const dt_node_t *dst,
>     [...]
> 
>     +   instr = BPF_MOV_REG(dst->dn_reg, src->dn_reg);
>     +   dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
>     -   instr = DIF_INSTR_FMT(DIF_OP_SLL,
>     -       src->dn_reg, reg, dst->dn_reg);
>     +   instr = BPF_ALU64_REG(BPF_LSH, dst->dn_reg, reg);
>         dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
> 
>     -   instr = DIF_INSTR_FMT((dst->dn_flags & DT_NF_SIGNED) ?
>     -       DIF_OP_SRA : DIF_OP_SRL, dst->dn_reg, reg, dst->dn_reg);
>     +   instr = BPF_ALU64_REG((dst->dn_flags & DT_NF_SIGNED) ?
>     +       BPF_ARSH : BPF_RSH, dst->dn_reg, reg);
> 
>         dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
>         dt_regset_free(drp, reg);
> 
> Note that the left- and right-shift instructions are converted to
> BPF;  meanwhile, the BPF_MOV_REG() instruction is introduced with
> no precedent in legacy DTrace.

That is not true...  as you can see in the code you quote, the DIF operation
to perform the shift uses a three-operand instruction using three registers:
one for the source value, one for the shift, and one for the destination.

BPF provides a two-operand shift instruction that modifies the value in the
provided register, as in: dst <<= reg.  So, to implement SLL (3-operand shigy
in DIF) in BPF, you first need to move src to dst, i.e.

	DIF: dst = src << reg;
	BPF: dst = src; dst <<= reg;

As such, in the general case for implementing SLL, you need a MOV + SHL in BPF.

The move was there in legacy DTrace, but it was part of the SLL implementation.
DIF-to-BPF cannot be done 1-to-1 at the instruction level because the
instruction sets are quite different.

As far as whether we should do this optimization here...  Let me think about
that a bit.  I plan on doing an optimization step later on before we do the
final assembly, and that would get rid of any MOV %rX, %rX already that tend to
be intrroduced in other places as well.  And even though the current uses do
not show a case where src != dst, I am not too sure that will continue to be
the case as we get further into implementing various language features and/or
code generation improvements.

E.g. it is possible we can do better code generation by using the value of an
expression that gets stored into a TLS variable directly from the register
that was used for the store instruvction rather than doing a fetch from the
TLS variable to get the value.  In that case, if the store to the TLS variable
involved a typecast of a value we still use afterwards. we definitely wouldn't
want the typecast to change the source register.

> Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
> ---
>  libdtrace/dt_cg.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index 0b8cd1d6..4f657967 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -1144,8 +1144,6 @@ dt_cg_typecast(const dt_node_t *src, const dt_node_t *dst,
>  
>  		dt_cg_setx(dlp, reg, n);
>  
> -		instr = BPF_MOV_REG(dst->dn_reg, src->dn_reg);
> -		dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
>  		instr = BPF_ALU64_REG(BPF_LSH, dst->dn_reg, reg);
>  		dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
>  
> -- 
> 2.18.4
> 
> 
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel



More information about the DTrace-devel mailing list