pytorch / executorch

On-device AI across mobile, embedded and edge for PyTorch
https://pytorch.org/executorch/
Other
2.2k stars 368 forks source link

Bug or feature? IntList double indirection on EValue program values #6946

Open corehalt opened 3 days ago

corehalt commented 3 days ago

🐛 Describe the bug

I converted a model into Edge dialect and finally serialized as a .pte file. Then I converted the .pte file into .json for closer inspection and realized that the IntList values looked like:

{
  "val_type": "IntList",
  "val": {
    "items": [
      195,
      196
    ]
  }
}

and these correspond to the values of the strides of a aten::convolution.out instruction. Turned out that these values are again references (indices) to another values in the EValue table of the schema, and they actually make reference to the expected values:

{
  "val_type": "Int",
  "val": {
    "int_val": 2
  }
},
{
  "val_type": "Int",
  "val": {
    "int_val": 2
  }
}

Is this by design? Are integer lists expected to be indices to another integer values? If so, what to expect when finding, for example, a list of boolean values as described on the schema? Double indirection only happens for integers?

table BoolList {
  items: [bool];
}

Versions

Any version?

metascroy commented 2 days ago

If you're curious about how program emission works, study https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py

During emission, emitted values are represented as "AbtractValue" (https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py#L148), which is an index into a value array. This is the double indirection you're observing. Arguments to a function are converted to abstract values (i.e., ints) by _emit_argument (https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py#L966). Because of this, the args for a kernel call are always a list of ints (https://github.com/pytorch/executorch/blob/main/exir/schema.py#L171), i.e., the ids of the abstract values that represent those args.

Perhaps @larryliu0820 or @JacobSzwejbka could say more.

corehalt commented 1 day ago

@metascroy Thank you! But what would happen with the BoolList? We cannot save indices to the values in the bool data type, no?

metascroy commented 19 hours ago

@metascroy Thank you! But what would happen with the BoolList? We cannot save indices to the values in the bool data type, no?

Maybe I'm not understanding your question. But let's follow what happens when an argument is a list of bools:

  1. In _emit_argument, we call _constant_to_evalue (https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py#L966)

  2. In _constant_to_evalue, we recongize the argument is a list of bools and we call _emit_list (https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py#L474)

  3. In _emit_list, we recognize the list elements are bools, and we return an EValue of BoolList (https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py#L294)

  4. This is passed back, and we're in _emit_argument again and _constant_to_evalue has returned an EValue of BoolList (where we started in step 1).

  5. We pass the value returned by _constant_to_evalue to _emit_evalue (https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py#L973). Note, in this example we are passing an EValue(BoolList).

  6. In _emit_evalue (https://github.com/pytorch/executorch/blob/main/exir/emit/_emitter.py#L524), we add the EValue(BoolList) to our values array and we return an AbstractValue that wraps the id representing this BoolList. This is what gets returned from _emit_argument. So the entire BoolList is represented by an int (i.e., the AbstractValue that references the id of the BoolList in the values array).