[RFC] Separate variables from SSA values in EmitC

Hi Gil, Thank you for the detailed RFC.

I thought about a variation of your second suggestion, that may work without having to work with symbols all the time. We could make lvalues part of the type system by introducing an !emitc.lvalue type. The emitc.automatic operation or a repurposed emitc.variable would produce a value which is wrapped in this type. The emitc.address_of op would be restricted to this type as well as the destination operand of the emitc.assign op. To make the usage of the value explicit in the IR I could imagine an emitc.lvalue_to_rvalue operation (similar to your emitc.read on symbols I guess) that represents lvalue conversion with a read memory effect.

Either way I am not quite sure how this would be handled in the emitter. Should the emitter skip the op and print the lvalue/symbol instead for every use? This would mean that the memory read is posponed to the uses in the generated code leading to a semantic mismatch between the IR and the generated code. Making all EmitC ops lvalue agnostic may be an option, we’d need to have a least custom func, call and return ops for this.

Example:

func.func @lvalue_variables(%v1: i32, %v2: i32) -> i32 {
  %val = emitc.mul %v1, %v2 : (i32, i32) -> i32
  %variable = emitc.variable : !emitc.lvalue<i32> // alloc effect
  emitc.assign %val : i32 to %variable : !emitc.lvalue<i32> // write effect
  %addr = emitc.address_of %variable : !emitc.lvalue<i32> -> !emitc.ptr<i32>
  emitc.call_opaque "zero" (%addr) : (i32) -> ()
  %updated_val = emitc.lvalue_to_rvalue %variable : i32 // read effect, (noop in emitter?)
  %neg_one = arith.constant -1 : i32
  emitc.assign %neg_one : i32 to %variable : !emitc.lvalue<i32> // invalidates %updated_val
  return %updated_val : i32
  // dealloc effect through automatic allocation scope
}

Simon