Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to write portable code? #51

Closed
peastman opened this issue Sep 17, 2014 · 15 comments
Closed

How to write portable code? #51

peastman opened this issue Sep 17, 2014 · 15 comments
Labels

Comments

@peastman
Copy link

I'm working on an application that needs to work on Linux, Mac, and Windows, both 32 and 64 bit. That covers several different ABIs. I'm trying to figure out how I can write my code in a way that will work on all of them.

I first wrote it on Mac and got everything working. Then I tried it on 64 bit Linux and that worked fine. But then I tried Windows (64 bit, but compiling a 32 bit executable) and it crashed. Then I tried 32 bit Linux and it also failed.

I believe this is due to differences in ABIs. For example, 64 bit platfoms use Xmm registers for floating point values, but most 32 bit platforms use Fp registers. But AsmJit requires me to use a completely different type of variable depending on which one is going to be used. As far as I can tell, there's no way to tell it, "This is a floating point variable. Use whatever type of register is appropriate."

How can I write my code in a way that will work on all platforms?

@refi64
Copy link
Contributor

refi64 commented Sep 17, 2014

#if defined(_M_X64) || defined(__amd64__) // 64-bit
typedef XmmReg FloatingReg;
#else // not 64-bit (hopefully 32-bit)
typedef FpReg FloatingReg;
#endif

@peastman
Copy link
Author

Unfortunately, it goes a lot deeper than that. You have to call completely different methods depending on what type of registers you're using. For example, if you're using Fp registers then you call fadd() to do addition, but if you're using Xmm registers then you call addsd().

@refi64
Copy link
Contributor

refi64 commented Sep 17, 2014

#if defined(_M_X64) || defined(__amd64__) // 64-bit
typedef XmmReg FloatingReg;
inline void addf(X86Assembler& a, FloatingReg& dst, FloatingReg& src) { a.addsd(dst, src); }
// repeat for other operations
#else // not 64-bit (hopefully 32-bit)
typedef FpReg FloatingReg;
inline void addf(X86Assembler& a, FloatingReg& dst, FloatingReg& src) { a.fadd(dst, src); }
// repeat for other operations
#endif

@peastman
Copy link
Author

Yes, I could write a completely new API that wraps AsmJit and provides an ABI agnostic interface. I was hoping there was a way to accomplish this without having to do that. :)

@kobalicek
Copy link
Member

I would go with SSE2 plus handling only differences in ABI. Asmjit::Compiler can help with that, but it's also fine in raw Assembler mode.

You can probably write your own abstractions on top of asmjit, which can help you with FPU/SSE, but such abstractions are not trivial as FPU provides a lot of things SSE doesn't and vice versa.

I'm currently not planning to develop any abstractions on top of asmjit as there is already LLVM, which does nice job when it comes to universality.

I think that x87 FPU is deprecated anyway, it's maybe a huge waste of time to even think of it :-)

@peastman
Copy link
Author

I would go with SSE2 plus handling only differences in ABI.

Could you explain what you mean? For example, if I tell it to use SSE registers for floating point values but I compile in 32 bit mode, then arguments don't get passed correctly to functions. How do I make that happen correctly?

@kobalicek
Copy link
Member

Well, this is what I meant by handling ABI differences. There is a class in asmjit called X86FuncDecl, which can be used to map argument to register or stack location. If you use only SSE2 the only problem would be to handle functions returning floating point (xmm0 in 64-bit mode and fp0 in 32-bit mode).

@peastman
Copy link
Author

How do I do that? To give a concrete example, here's a utility function I've written. It generates calls to functions that take a double as their argument and return a double.

void generateSingleArgCall(X86Compiler& c, X86XmmVar& dest, X86XmmVar& arg, double (*function)(double)) {
    X86GpVar fn(c, kVarTypeIntPtr);
    c.mov(fn, imm_ptr((void*) function));
    X86CallNode* call = c.call(fn, kFuncConvHost, FuncBuilder1<double, double>());
    call->setArg(0, arg);
    call->setRet(0, dest);
}

I see that X86Compiler::call() returns an X86CallNode, whose getDecl() method returns an X86FuncDecl. So I take it that it's already handling the arguments correctly? What do I need to add to get the return value into dest?

@kobalicek
Copy link
Member

Actually returning a float/double is still an issue in 32-bit mode, the oldest open Issue #3 is not closed by because of this. I didn't fix this simply because I have never needed to return float/double from Compiler, but yeah, I should finally fix this one.

However, function parameters (float / double) should be handled properly in both 32-bit and 64-bit modes.

@peastman
Copy link
Author

If you can make returning floating point values work, that would be awesome. My application is all about floating point. More specifically, I'm adding JIT support to Lepton (https://simtk.org/home/lepton) for use in OpenMM (https://simtk.org/home/openmm).

@kobalicek
Copy link
Member

Yeah, I will definitely take a look and update the issue

@kobalicek
Copy link
Member

Peastman: Returning is now supported, I'm closing this for now as there is currently nothing to fix.

@peastman
Copy link
Author

peastman commented Feb 9, 2015

That's great news! I'll give it a try.

@peastman
Copy link
Author

Using the latest version of asmjit from the repository and compiling in 32 bit mode, my program crashes when I try to execute the compiled code. If I compile in 64 bit mode it still works fine.

Is identical code supposed to work in both modes? Or do I need to do something differently in 32 bit mode?

@kobalicek
Copy link
Member

It should work the same, it's a bug if it doesn't.

I have just added this feature recently so it's possible it's still buggy, if you have any code that crashes fill a new issue and I will try to fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants