jasmin icon indicating copy to clipboard operation
jasmin copied to clipboard

Mutable arguments that are not meant to be returned

Open eponier opened this issue 2 years ago • 5 comments

Apparently before PR https://github.com/jasmin-lang/jasmin/pull/82, some people were relying on a pattern involving inline functions and reg ptr arguments used as a scratchpad. Here is a small example.

inline fn f (reg ptr u64[1] a, reg ptr u64[1] scratchpad) -> reg ptr u64[1] {
  reg u64 tmp;
  scratchpad[0] = 2;
  tmp = scratchpad[0];
  a[0] = tmp;
  return a;
}

export fn main () -> reg u64 {
  stack u64[1] a scratchpad;
  reg u64 res;
  a[0] = 1;
  scratchpad[0] = 1;
  a = f (a, scratchpad);
  res = a[0];
  return res;
}

Function f takes as an argument variable scratchpad that is used to write temporary data and is useless after the function call, that's why it's not returned. This example used to work because:

  • scratchpad is not returned so is marked as const
  • the function is inline, otherwise it would fail at line scratchpad[0] = 2; during stack alloc
  • we don't read scratchpad after the call

With PR https://github.com/jasmin-lang/jasmin/pull/82, typing fails on scratchpad[0] = 2; since writing in a const array is now forbidden. Do we want this feature back? I don't think we want it back as is, because it was hacky. But one thing we could do is allowing reg mut ptr not to be returned, meaning such a variable would be unusable after the function call. Maybe this would allow to reduce the lifetime of the variable because we wouldn't have to insert an assignment after the function call. And it could be supported also for non-inline functions.

eponier avatar May 02 '22 14:05 eponier

cc @AntoineSere who mentioned that problem to me. Did I describe the issue faithfully?

eponier avatar May 02 '22 15:05 eponier

Yes, the issue seems to me to be accurately described.

AntoineSere avatar May 03 '22 10:05 AntoineSere

Are there any good reasons to use this kind of pattern? It seems that the inline function could declare its own local stack variable instead of receiving it from the caller.

vbgl avatar May 16 '22 10:05 vbgl

If the same scratchpad can be used by different inlined functions, then this construct makes sense, as it allow you to only allocate one scratchpad instead of many if you were to declare a local stack variable in each inlined function.

Here is a small modification of @eponier 's example illustrating this usecase:

inline fn f (reg ptr u64[1] a, reg ptr u64[1] scratchpad) -> reg ptr u64[1] {
  reg u64 tmp;
  scratchpad[0] = 1;
  tmp = scratchpad[0];
  a[0] = tmp;
  return a;
}

inline fn g (reg ptr u64[1] a, reg ptr u64[1] scratchpad) -> reg ptr u64[1] {
  reg u64 tmp;
  scratchpad[0] = 2;
  tmp = scratchpad[0];
  a[0] = tmp;
  return a;
}

export fn main () -> reg u64 {
  stack u64[1] a scratchpad;
  reg u64 res;
  a[0] = 1;
  scratchpad[0] = 1;
  a = f (a, scratchpad);
  a = g (a, scratchpad);
  res = a[0];
  return res;
}

In this example, only one scratchpad is needed.

AntoineSere avatar May 16 '22 12:05 AntoineSere

The compiler does that for you.

/* --------------------------------------------------- */
inline fn f (reg ptr u64[1] a, reg ptr u64[1] scratchpad) -> reg ptr u64[1], reg ptr u64[1] {
  reg u64 tmp;
  scratchpad[0] = 1;
  tmp = scratchpad[0];
  a[0] = tmp;
  return a, scratchpad;
}

inline fn g (reg ptr u64[1] a, reg ptr u64[1] scratchpad) -> reg ptr u64[1], reg ptr u64[1] {
  reg u64 tmp;
  scratchpad[0] = 2;
  tmp = scratchpad[0];
  a[0] = tmp;
  return a, scratchpad;
}

#[stacksize=16]
export fn main () -> reg u64 {
  stack u64[1] a scratchpad;
  reg u64 res;
  a[0] = 1;
  // scratchpad[0] = 1;
  a, scratchpad = f (a, scratchpad);
  a, _ = g (a, scratchpad);
  res = a[0];
  return res;
}

/* --------------------------------------------------- */
inline fn h (reg ptr u64[1] a) -> reg ptr u64[1] {
  reg u64 tmp;
  stack u64[1] scratchpad;
  scratchpad[0] = 1;
  tmp = scratchpad[0];
  a[0] = tmp;
  return a;
}

inline fn i (reg ptr u64[1] a) -> reg ptr u64[1] {
  reg u64 tmp;
  stack u64[1] scratchpad;
  scratchpad[0] = 2;
  tmp = scratchpad[0];
  a[0] = tmp;
  return a;
}

#[stacksize=16]
export fn good () -> reg u64 {
  stack u64[1] a;
  reg u64 res;
  a[0] = 1;
  a = h (a);
  a = i (a);
  res = a[0];
  return res;
}

vbgl avatar May 16 '22 12:05 vbgl