StanzaOrg / lbstanza-old

L.B. Stanza Programming Language
Other
216 stars 23 forks source link

lostanza - passing/return struct by value in C #174

Open callendorph opened 1 year ago

callendorph commented 1 year ago

In C - you can pass or return a defined struct by value from a function. For example - see the GSL complex math library.

I can't seem to work out how to do this in lostanza. It seems like lostanza is focused only on pointers. Do you have an example of using structures as values?

OlegPliss commented 1 year ago

This is a working code from current GC implementation passing and returning small structures by value: `lostanza deftype LiveRange : live:ptr dead:ptr

;Write the live range r into the heap address at the given break location. ; ;Live ranges are stored directly in the heap memory, in the space ;currently occupied by dead objects: the breaks. ; ;The encoding varies depending upon the size of the break. ;The minimum size of a break is 8 bytes long, but the live range struct is ;in general 16 bytes long. ; ;Encoding: ;Case break >= 16 bytes: then just directly store the live range. ;Case break == 8 bytes: only store the 'dead' field, tagged with 1. ; ;Since heap addresses are always 8-byte aligned, the tagged 1 will tell us ;which encoding we are using. lostanza defn write-live-range (location:ptr, r:LiveRange) -> ref : ;Case: break == 8 bytes. if addr(location[1]) == r.live : ;Store the tagged 'dead' field. location[0] = (r.dead as long) | 1 ;Case: break >= 16 bytes. else : ;Store the live range directly. location[0] = r.live as long location[1] = r.dead as long ;No meaningful return value return false

;Read the live range at the given break location. lostanza defn read-live-range (location:ptr) -> LiveRange : ;Read the first word at the location. val location0 = location[0] ;Test the tag bit to determine which encoding we are using. if location0 & 1L : ;Case break == 8 bytes. val live = addr(location[1]) ;Computed from size of break. val dead = (location0 - 1) as ptr ;Remove tag bit. return LiveRange{live, dead} else : ;Case break >= 16 bytes. val live = location0 as ptr val dead = location[1] as ptr return LiveRange{live, dead} `

callendorph commented 1 year ago

Sorry - I should have been more specific with what I am trying to do. What I want to do is wrap GSL's complex value implementation. It passes a struct gsl_complex by value to and from functions in C.

The gsl_complex is a struct containing 2 doubles (128 bits). From what I can tell of call-c's calling convention - it can only assign to a value of a primitive type or a pointer (or at least that is what I have gathered from this document). Maybe I'm wrong on that account ?

It is not clear to me how I can use the LiveRange code here to make a call-c invokation of say gsl_complex_polar and get the resulting value as a lostanza deftype.

Below has gotten me the farthest towards my goal:

public lostanza deftype GComplex :
  x: double
  y: double

extern gsl_complex_polar : (double, double) -> GComplex

lostanza var r:GComplex

public lostanza defn GComplex (rad:ref<Double>, th:ref<Double>) -> ref<GComplex> :
  r = call-c gsl_complex_polar(rad.value, th.value)
  return new GComplex{r.x, r.y}

This sadly fails with FATAL ERROR: More than a single C return value!

Is there another way to do this ?

CuppoJava commented 1 year ago

Hi Carl,

What you are trying to do is reasonable, and is the way that it should (and will ultimately) be done. You're hitting a part of the compiler that is not-yet-implemented today: the C calling convention for structs.

Until that is finished, you have to write an additional C-wrapper that changes how gsl_complex_polar returns its results in order to connect to it.

Our apologies. It's on the near-term roadmap however!