[PATCH 2/5] d3dcompiler: Introduce a separate structure for lvalue derefs.

Zebediah Figura zfigura at codeweavers.com
Fri Apr 17 11:48:42 CDT 2020


On 4/17/20 2:03 AM, Matteo Bruni wrote:
> On Wed, Apr 15, 2020 at 2:41 AM Zebediah Figura <z.figura12 at gmail.com> wrote:
>>
>> From: Zebediah Figura <zfigura at codeweavers.com>
>>
>> Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
>> ---
>> aaa625217, and the approach it implies, works, but on further reflection it's
>> still not very pretty. For example, the following line of HLSL:
>>
>> var.a.b = 2.0;
>>
>> produces the following IR:
>>
>> 2: 2.0
>> 3: deref(var)
>> 4: @3.b
>> 5: @4.c = @2
>>
>> This essentially works, provided that the codegen layer knows how to unwind a
>> deref chain, but it's pretty janky. Node 4 implies we're reading from node 3,
>> and node 3 implies we're reading from var, neither of which is actually
>> happening. The RA pass can't easily be smart enough to recognize that 4 doesn't
>> need to read from 3, which is a problem if we introduce a "maybe uninitialized"
>> warning.
>>
>> Moreover, if we introduce DCE, we can't DCE out node 3 because of node 4, and
>> while we could DCE out node 4 in terms of removing it from the list, we can't
>> actually destroy the node, since node 5 is using it. Having nodes not in the
>> list is also janky, I feel.
>>
>> With this patch we instead get the following IR:
>>
>> 2: 2.0
>> 3: deref(var)
>> 4: @3.b
>> 5: @4.c
>> 6: deref(var).b.c = @2
>>
>> We still get redundant reads, but at least this time we can easily DCE them
>> out. Needing less sanity checks in RA is also nice.
>>
>>   dlls/d3dcompiler_43/d3dcompiler_private.h | 21 +++++-
>>   dlls/d3dcompiler_43/hlsl.y                | 28 +++++---
>>   dlls/d3dcompiler_43/utils.c               | 83 ++++++++++++++++-------
>>   3 files changed, 97 insertions(+), 35 deletions(-)
> 
> Hi Zeb,
> 
> after reading it again and actually getting into "review mode", I have
> some more thoughts... Sorry for not thinking of this earlier.
> See below.
> 
>> diff --git a/dlls/d3dcompiler_43/d3dcompiler_private.h b/dlls/d3dcompiler_43/d3dcompiler_private.h
>> index 1f1d8e26637..8f81271383b 100644
>> --- a/dlls/d3dcompiler_43/d3dcompiler_private.h
>> +++ b/dlls/d3dcompiler_43/d3dcompiler_private.h
>> @@ -873,10 +873,29 @@ struct hlsl_ir_deref
>>       struct hlsl_deref src;
>>   };
>>
>> +struct hlsl_lvalue
>> +{
>> +    enum hlsl_ir_deref_type type;
>> +    union
>> +    {
>> +        struct hlsl_ir_var *var;
>> +        struct
>> +        {
>> +            struct hlsl_lvalue *array;
>> +            struct hlsl_ir_node *index;
>> +        } array;
>> +        struct
>> +        {
>> +            struct hlsl_lvalue *record;
>> +            struct hlsl_struct_field *field;
>> +        } record;
>> +    } v;
>> +};
>> +
>>   struct hlsl_ir_assignment
>>   {
>>       struct hlsl_ir_node node;
>> -    struct hlsl_deref lhs;
>> +    struct hlsl_lvalue lhs;
>>       struct hlsl_ir_node *rhs;
>>       unsigned char writemask;
>>   };
>> diff --git a/dlls/d3dcompiler_43/hlsl.y b/dlls/d3dcompiler_43/hlsl.y
>> index d3ef18bb6e9..5366181f356 100644
>> --- a/dlls/d3dcompiler_43/hlsl.y
>> +++ b/dlls/d3dcompiler_43/hlsl.y
>> @@ -2642,16 +2642,16 @@ static unsigned int index_instructions(struct list *instrs, unsigned int index)
>>   }
>>
>>   /* Walk the chain of derefs and retrieve the actual variable we care about. */
>> -static struct hlsl_ir_var *hlsl_var_from_deref(const struct hlsl_deref *deref)
>> +static struct hlsl_ir_var *hlsl_var_from_lvalue(const struct hlsl_lvalue *deref)
>>   {
>>       switch (deref->type)
>>       {
>>           case HLSL_IR_DEREF_VAR:
>>               return deref->v.var;
>>           case HLSL_IR_DEREF_ARRAY:
>> -            return hlsl_var_from_deref(&deref_from_node(deref->v.array.array)->src);
>> +            return hlsl_var_from_lvalue(deref->v.array.array);
>>           case HLSL_IR_DEREF_RECORD:
>> -            return hlsl_var_from_deref(&deref_from_node(deref->v.record.record)->src);
>> +            return hlsl_var_from_lvalue(deref->v.record.record);
>>       }
>>       assert(0);
>>       return NULL;
>> @@ -2674,7 +2674,7 @@ static void compute_liveness_recurse(struct list *instrs, unsigned int loop_firs
>>           case HLSL_IR_ASSIGNMENT:
>>           {
>>               struct hlsl_ir_assignment *assignment = assignment_from_node(instr);
>> -            var = hlsl_var_from_deref(&assignment->lhs);
>> +            var = hlsl_var_from_lvalue(&assignment->lhs);
>>               if (!var->first_write)
>>                   var->first_write = loop_first ? min(instr->index, loop_first) : instr->index;
>>               assignment->rhs->last_read = instr->index;
>> @@ -2693,10 +2693,22 @@ static void compute_liveness_recurse(struct list *instrs, unsigned int loop_firs
>>           case HLSL_IR_DEREF:
>>           {
>>               struct hlsl_ir_deref *deref = deref_from_node(instr);
>> -            var = hlsl_var_from_deref(&deref->src);
>> -            var->last_read = loop_last ? max(instr->index, loop_last) : instr->index;
>> -            if (deref->src.type == HLSL_IR_DEREF_ARRAY)
>> -                deref->src.v.array.index->last_read = instr->index;
>> +
>> +            switch (deref->src.type)
>> +            {
>> +                case HLSL_IR_DEREF_VAR:
>> +                    deref->src.v.var->last_read = loop_last ? max(instr->index, loop_last) : instr->index;
>> +                    break;
>> +
>> +                case HLSL_IR_DEREF_ARRAY:
>> +                    deref->src.v.array.array->last_read = instr->index;
>> +                    deref->src.v.array.index->last_read = instr->index;
>> +                    break;
>> +
>> +                case HLSL_IR_DEREF_RECORD:
>> +                    deref->src.v.record.record->last_read = instr->index;
>> +                    break;
>> +            }
>>               break;
>>           }
>>           case HLSL_IR_EXPR:
>> diff --git a/dlls/d3dcompiler_43/utils.c b/dlls/d3dcompiler_43/utils.c
>> index d24341329d3..519f50612e8 100644
>> --- a/dlls/d3dcompiler_43/utils.c
>> +++ b/dlls/d3dcompiler_43/utils.c
>> @@ -1478,27 +1478,41 @@ static unsigned int invert_swizzle(unsigned int *swizzle, unsigned int writemask
>>       return new_writemask;
>>   }
>>
>> -static BOOL validate_lhs_deref(const struct hlsl_ir_node *lhs)
>> +static BOOL get_assignment_lhs(struct hlsl_lvalue *dst, struct hlsl_ir_node *node)
>>   {
>>       struct hlsl_ir_deref *deref;
>>
>> -    if (lhs->type != HLSL_IR_DEREF)
>> +    if (node->type != HLSL_IR_DEREF)
>>       {
>> -        hlsl_report_message(lhs->loc, HLSL_LEVEL_ERROR, "invalid lvalue");
>> +        hlsl_report_message(node->loc, HLSL_LEVEL_ERROR, "invalid lvalue");
>>           return FALSE;
>>       }
>>
>> -    deref = deref_from_node(lhs);
>> +    deref = deref_from_node(node);
>> +    dst->type = deref->src.type;
>>
>> -    if (deref->src.type == HLSL_IR_DEREF_VAR)
>> -        return TRUE;
>> -    if (deref->src.type == HLSL_IR_DEREF_ARRAY)
>> -        return validate_lhs_deref(deref->src.v.array.array);
>> -    if (deref->src.type == HLSL_IR_DEREF_RECORD)
>> -        return validate_lhs_deref(deref->src.v.record.record);
>> +    switch (deref->src.type)
>> +    {
>> +        case HLSL_IR_DEREF_VAR:
>> +            dst->v.var = deref->src.v.var;
>> +            return TRUE;
>>
>> -    assert(0);
>> -    return FALSE;
>> +        case HLSL_IR_DEREF_ARRAY:
>> +            dst->v.array.index = deref->src.v.array.index;
>> +            if (!(dst->v.array.array = d3dcompiler_alloc(sizeof(*dst->v.array.array))))
>> +                return FALSE;
>> +            return get_assignment_lhs(dst->v.array.array, deref->src.v.array.array);
>> +
>> +        case HLSL_IR_DEREF_RECORD:
>> +            dst->v.record.field = deref->src.v.record.field;
>> +            if (!(dst->v.record.record = d3dcompiler_alloc(sizeof(*dst->v.record.field))))
>> +                return FALSE;
>> +            return get_assignment_lhs(dst->v.record.record, deref->src.v.record.record);
>> +
>> +        default:
>> +            assert(0);
>> +            return FALSE;
>> +    }
>>   }
> 
> I'm somewhat annoyed by this, it feels like we could directly create
> lvalue nodes while parsing... Except there doesn't seem to be any
> simple way. So I'm going to try and keep my initial knee-jerk reaction
> at bay.
> 
> On the other hand, I think it would be nicer to enforce a flat IR
> structure. Instead of having an arbitrarily complex lvalue inside the
> assignment instruction, maybe it should have the same structure of a
> deref, where each "level" is a separate instruction. This is probably
> why I was thinking of a flag rather than a separate node type: when
> constructing the assignment you would traverse the lhs derefs and turn
> them into lvalues. Although having a separate node type and changing
> that works as well (and it's probably nicer with liveness computation
> and other passes).
> 
> Does that sound reasonable? Am I missing anything?

Well, note that one problem is we kind of have to (recursively) dup the 
lvalue anyway, so that complex assignments work.

I think I understand why flattening is desirable in general, but it's 
not clear to me how that would apply to lvalues (or even to rvalue deref 
chains, honestly), since at codegen time we essentially resolve 
arbitrarily complex deref chains into one register (well, except for 
non-constant array indexing...)

It's also not obvious to me that's prettier to flatten it, given on the 
other hand we're also now inserting nodes into the IR list that would 
presumably not participate in RA or generate code.

Maybe I'm misunderstanding what you're trying to propose here, though?

>> +static void debug_dump_lvalue(const struct hlsl_lvalue *deref)
>> +{
>> +    switch (deref->type)
>> +    {
>> +        case HLSL_IR_DEREF_VAR:
>> +            debug_dump_ir_var(deref->v.var);
>> +            break;
>> +        case HLSL_IR_DEREF_ARRAY:
>> +            debug_dump_lvalue(deref->v.array.array);
>> +            wine_dbg_printf("[");
>> +            debug_dump_src(deref->v.array.index);
>> +            wine_dbg_printf("]");
>> +            break;
>> +        case HLSL_IR_DEREF_RECORD:
>> +            debug_dump_lvalue(deref->v.record.record);
>> +            wine_dbg_printf(".%s", debugstr_a(deref->v.record.field->name));
>> +            break;
>> +    }
>> +}
> 
> If we go with my idea, we probably need something to disambiguate
> lvalues from rvalues / plain derefs.
> 




More information about the wine-devel mailing list