Most efficient way for a function to return two types?

I apologize if this is the wrong list. I’m writing a language and outputting llvm-ir instead of using the c api. I looked at clang’s output a few times but I’m a little unsure what to do in this case

As an example I have a function called itoa which takes an int and returns a string. Strings in my language has 2 fields, the pointer and byte length. On a 64bit machine it’d be returning a 64bit pointer and 64bit int value. My question is HOW should I do this if I want to generate fast code? And a follow up is should I do it that way if I want to support most architectures?

Currently my function signature is define {i8*, i64} @itoa(i64) {. Should I return an i8* instead and have an int pointer? (i64*). I don’t see an ‘out’ parameter attributes or anything to hint to the optimizer the value at the address can be uninitialized. https://llvm.org/docs/LangRef.html#parameter-attributes. If I pass in a string should I do it the same way as returning? Currently I do. I pass in {i8*, i64} as a parameter and been thinking I can pass in i8* and i64 instead (note that i64 is not a pointer this time. It may be if the variable is mutable).

I’d probably do it the same way clang does for

struct str { char* ptr; int64_t length; };
str itoa(…);

But realize that LLVM IR isn’t ABI neutral - Clang may generate significantly different IR when targeting different architectures to match their ABI. Even if you don’t need to match these ABIs (you don’t need your code to be able to call into/be called from C or C++ source code compiled with Clang or GCC, etc), just getting the machine code you desire (what parts of a struct get passed in which registers (or memory), etc) may require various per-architecture handling on your part/in how you generate the IR.

  • Dave

Also note that “most efficient” depends on the application and use case. On x86-64, for error handling use cases where you want to return the same struct of two pointers up several calls, I found that using the non-trivial ABI (struct is on the stack, pointer to the struct is in a register) was faster than the trivial ABI (object is returned in two registers). You will need to benchmark to see which is best for your anticipated use cases.