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

Fptest64 #8

Open
wants to merge 3 commits into
base: fptest
Choose a base branch
from
Open

Fptest64 #8

wants to merge 3 commits into from

Conversation

jgbaldwinbrown
Copy link

Fptest64 was branched off of fptest. It is identical except that it modifies the 64-bit floating point routines from Berkeley softfloat 2 to use pointers instead of values, allowing these to be compiled by cc65. All functions that previously took a float64 as an argument now take a *float64 as an argument. All functions that returned a struct (either a float64 or a commonNaNT) now accept an additional argument, dst, which is a pointer to memory for returning the value.

There's also one small change in roundAndPackFloat32 that fixes a problem I encountered in small test programs on the Commodore 64.

Note that this differs from the approach for returning commonNaNTs in the 32-bit floating point routines. Those return a pointer to static memory containing the commonNaNT, which means each invocation of the function clobbers the return value of the previous invocation. The approach used here trades inconvenience for safety by avoiding this clobbering.

Note also that, to reduce memory copying, some functions modify the pointed-to return memory during the function and will produce incorrect results if a memory location is used both as an input argument and a return location. To avoid problems, don't do this:

float64 a, b;
float64_add(&a, &a, &b);

Instead, do this:

float64 a, b, c;
float64_add(&c, &a, &b);
a = c;

The functions included here should work, though they require more testing. Fully integrating them into the compiler will require further work:

  • adding assembly language hooks to use the functions
  • handling 64-bit floating point values in the AST
  • adding generator code to use the functions in compiled programs
  • figuring out how to pass pointers instead of 8-byte values in generated code

For this last problem, I can think of 2 solutions:

  1. Use a separate stack just to hold 8-byte values and pass pointers between functions
  2. Update the calling convention to allow passing 8-byte values. This should be relatively easy with the cdecl calling convention, but with fastcall, we will probably have to add a new pseudo-register on the zero page, tentatively labeled sreg2, which would be 4 bytes in size.

I am not that familiar with the compiler, but I'll keep working on these problems for a future pull request.

-Jim

@mrdudz
Copy link
Owner

mrdudz commented Apr 16, 2024

looks like some style issues need to be fixed :)

@jgbaldwinbrown
Copy link
Author

Yep, I found a dangling space in specialize.h. It's fixed now and all tests pass!

@mrdudz
Copy link
Owner

mrdudz commented May 15, 2024

OK, so i looked a bit closer... Unfortunately i see one or two problems with this. I really appreciate the effort - however, i'd rather keep the unmodified code in my branch as long as the modifications are not exactly like we need them.

  • Changing the ABI for 64 bit floats to pass pointers to the 64bit values, instead of passing 64bit values (which implies adding support for this to the compiler first), sure looks like the obvious thing to do - however, there might be some subtle detail in how C is supposed to work that makes this a bad thing to do. I honestely can't tell, @acqn @oliverschmidt @spiro-trikaliotis @kugelfur et al please comment

: another thing to consider is, that at some point in the future, the float support should be extended so it can make use of eg a library with 16bit floats. This would be a lot easier to handle, if floats and doubles share the same ABI (ie no passing of doubles via pointers), because then the existing 32bit float library code can be used for the doubles in that case.

  • besides the parameter passing, the 64bit functions must match the described API for 32bit functions exactly. ie your described behaviour for a = a X b is not acceptable, it must be possible to do this, the code generator will become a mess if it cant handle the two cases exactly alike.

@oliverschmidt
Copy link

Unfortunately this topic as well beyond my skill level. I won't be able to provide any helpful assessment 😞

@jgbaldwinbrown
Copy link
Author

Hi Bob! I agree with everything you're saying and can see why you wouldn't want to accept the pull request in its current state. I'm happy to change the float64 functions to exactly match the float32 functions (i.e., making the a = a X b case work correctly). I think the trickier thing is the problem you brought up first -- for 64-bit floats to be used in functions, there needs to be a way to pass 8-byte values between functions. If the compiler secretly passes pointers instead of the full 8 bytes of the float64, a user could do something like this:

#include <stdio.h>

double modify_incoming(double x) { // here, a pointer to x is passed instead of a copy of x.
	x += 5.0;
	return x;
}

int main() {
	double x = 8.0;
	double y = modify_incoming(x);

	printf("%f, %f\n", x, y);
	// this should print "8.0, 13.0", but it will actually print "13.0, 13.0".

	return 0;
}

To solve this problem, we would either need to extend the calling convention of cc65 to allow passing 8-byte values as arguments and return values (see my previous post for ways to do that), or we would need to do the code generation such that, when doubles are passed, a temporary variable is put on the stack to use in place of the incoming argument.

For example, the user code above could generate code with the necessary stack-allocated temporary variables internally, like so:

#include <stdio.h>

void modify_incoming(double *dst, double *x) {
	double _temp = *x; // a temporary variable is inserted here to hold x's value.
	_temp += 5.0;
	*dst = _temp;
}

int main() {
	double y;
	double x = 8.0;
	modify_incoming(&y, &x); // this is changed to pass by pointer.

	printf("%f, %f\n", x, y);
	// this should print "8.0, 13.0", and now it actually will.

	return 0;
}

Overall, I think this would make code generation for 8-byte values complicated. I think updating the calling convention to allow for 8-byte argument passing would be the cleanest solution, but I don't think I can do that by myself. I'd be happy to help with that as much as I can, but that seems like a big increase in the scope of the project compared to implementing 32-bit floats. For now, I can get the float64 functions to work safely with the a = a X b case, and then the issue of fully implementing doubles in the compiler could be addressed later. What do you think?

@mrdudz
Copy link
Owner

mrdudz commented Jun 22, 2024

For now, I can get the float64 functions to work safely with the a = a X b case, and then the issue of fully implementing doubles in the compiler could be addressed later. What do you think?

For the reasons already mentioned, i think it only makes sense without that magic converting to pointers (That will create more problems than it solves).

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

Successfully merging this pull request may close these issues.

3 participants