dmd icon indicating copy to clipboard operation
dmd copied to clipboard

No predefined version for 64bit ?!

Open dlangBugzillaToGithub opened this issue 1 year ago • 18 comments

Manu reported this on 2024-02-26T06:28:11Z

Transferred from https://issues.dlang.org/show_bug.cgi?id=24412

CC List

  • Walter Bright (@WalterBright)
  • Dennis
  • Iain Buclaw (@ibuclaw)

Description

Surprisingly, I was looking for a version that identifies if the target arch is 64bit, and there's no such thing!

I need to know if the target has 64bit registers and can perform 64bit arithmetic. It's surprising there's nothing that identifies this.

Please add a new predefined version.

dlangBugzillaToGithub avatar Feb 26 '24 06:02 dlangBugzillaToGithub

dkorpel commented on 2024-02-26T17:56:56Z

The usual check for this is `static if (size_t.sizeof == 8)`.

dlangBugzillaToGithub avatar Feb 26 '24 17:02 dlangBugzillaToGithub

turkeyman commented on 2024-02-26T19:16:58Z

That would tell you if you have 64bit address space. Not the same thing.

dlangBugzillaToGithub avatar Feb 26 '24 19:02 dlangBugzillaToGithub

dkorpel commented on 2024-02-27T11:59:36Z

It usually is, so people tend to use it that way. Are you programming for the Nintendo 64 perhaps? :)

I'm looking how other languages check for 64-bit registers, and in C, it seems like people also query sizes of pointer / integer types, or query a specific ISA (like x86-64): https://stackoverflow.com/a/33867847/8411666

So without precedence, the name could be something like 'D_64bitRegister' or 'D_64bitArithmetic'. But what do you want to do with this information exactly? Is this to provide your own alternative 64-bit emulation when there's no 'native' 64-bit math?

Because that could be tricky: consider WebAssembly without wasm64. It has 32-bit pointers and 64-bit math instructions, but it's not an actual processor architecture: the speed depends on the execution environment, so would 'D_64bitRegister' be set in that case?

dlangBugzillaToGithub avatar Feb 27 '24 11:02 dlangBugzillaToGithub

turkeyman commented on 2024-02-27T12:38:59Z

> It usually is, so people tend to use it that way.  

But it's not actually the same thing, and people using it that way have likely written a bug...

> Are you programming for the Nintendo 64 perhaps? :)

I do have a lifetime of experience writing software for such 64bit systems with 32bit pointers. They are a real thing that exists.

> [...] or query a specific ISA [...]

Yes, in C++ we wrangle this information ourselves from whatever material the compiler makes available to determine the facts. Everyone has such a global header file that does this wrangling. I'm specifically looking to avoid that here.

> Is this to provide your own alternative 64-bit emulation when there's no 'native' 64-bit math?

Exactly; if there are no 64bit registers, it can be the case that some alternative algorithm is superior. There do exist algorithms that only work efficiently with 64bits.

> [...] consider WebAssembly without wasm64 [...] would 'D_64bitRegister' be set in that case?

Maybe not define it for webasm since 32bit machines can run web browsers too. I mean, you're running in a web browser; your concern about maximising architectural perf is already forfeit.
It's difficult to imagine a case where this could be used where it's a compatibility issue; it's basically for optimisation opportunities, and nothing else.

dlangBugzillaToGithub avatar Feb 27 '24 12:02 dlangBugzillaToGithub

dkorpel commented on 2024-02-27T13:19:07Z

> It's difficult to imagine a case where this could be used where it's a compatibility issue; it's basically for optimisation opportunities, and nothing else.

Since versions are usually about compatibility (i.e. `version(HasFeatureX) useFeatureX()`), perhaps it would be better to define a 'fast int' type in druntime, somewhat similar to C's `int_fast32_t` or `intmax_t`, but one that represents the largest integer type with native math instructions (excluding SIMD). That would also generalize better to 16 and 128 bit. Would that work?

dlangBugzillaToGithub avatar Feb 27 '24 13:02 dlangBugzillaToGithub

turkeyman commented on 2024-02-27T15:00:33Z

I think that's over-complicating it. Version is fine.

dlangBugzillaToGithub avatar Feb 27 '24 15:02 dlangBugzillaToGithub

bugzilla (@WalterBright) commented on 2024-03-26T04:46:02Z

https://dlang.org/spec/version.html

Says:

```
    D_LP64	Pointers are 64 bits (command line switch -m64). (Do not confuse this with C's LP64 model)
    D_X32	Pointers are 32 bits, but words are still 64 bits (x32 ABI) (This can be defined in parallel to X86_64)
```

What the implementation actually does is:

```
    if (tgt.isX86_64)
    {
        VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64");
        VersionCondition.addPredefinedGlobalIdent("X86_64");
    }
    else
    {
        VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy
        VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86");
        VersionCondition.addPredefinedGlobalIdent("X86");
    }
    if (tgt.isLP64)
        VersionCondition.addPredefinedGlobalIdent("D_LP64");
    else if (tgt.isX86_64)
        VersionCondition.addPredefinedGlobalIdent("X32");
```

Note how something simple turned into a monstrosity that nobody can figure out, and the documentation is both wrong and incomplete. Sigh.

I suspect that the undocumented X32 is what Manu is looking for, but dmd never generates it. Does gdc or ldc?

dlangBugzillaToGithub avatar Mar 26 '24 04:03 dlangBugzillaToGithub

ibuclaw (@ibuclaw) commented on 2024-03-26T14:36:55Z

(In reply to Dennis from comment #1)
> The usual check for this is `static if (size_t.sizeof == 8)`.

`version(D_LP64)` would be a better alternative to that.

dlangBugzillaToGithub avatar Mar 26 '24 14:03 dlangBugzillaToGithub

ibuclaw (@ibuclaw) commented on 2024-03-26T15:06:52Z

(In reply to Walter Bright from comment #7)
> https://dlang.org/spec/version.html
> 
> Says:
> 
> ```
>     D_LP64	Pointers are 64 bits (command line switch -m64). (Do not confuse
> this with C's LP64 model)
>     D_X32	Pointers are 32 bits, but words are still 64 bits (x32 ABI) (This
> can be defined in parallel to X86_64)
> ```
Introduced by https://github.com/dlang/dlang.org/pull/175

As one comment pointed out:
"""
X32 is a specific implementation for x86-64 hardware, but the description refers to an abstract concept
"""

Adding "x32 ABI" was the compromise, but I don't think it's explicit enough.

Pick any wiki, and the description is better:
"""
X32 is an alternative ABI for x86-64 that uses the full 64-bit x86-64 instruction and register set and 32-bit pointers and longs.
"""
https://sourceware.org/glibc/wiki/x32

"""
The x32 ABI provides 32-bit integers, long and pointers (ILP32) on Intel and AMD 64-bit hardware
"""
https://en.wikipedia.org/wiki/X32_ABI

> Note how something simple turned into a monstrosity that nobody can figure
> out, and the documentation is both wrong and incomplete. Sigh.
> 
> I suspect that the undocumented X32 is what Manu is looking for, but dmd
> never generates it. Does gdc or ldc?
That is a typo in dmd, introduced by https://github.com/dlang/dmd/pull/12508

D_X32 is predefined by gdc on X32 targets, itself is now a deprecated ABI of X86_64.  I had and still have doubts that DMD would support such a `-target` anyway.

A more informative version identifier for it would have been X86_64_X32, but this was added at a time where brevity was preferred over clarity for some reason. Which also left in the spec D_AVX and D_AVX2 (yuck!) instead of X86_64_AVX and X86_64_AVX2.

dlangBugzillaToGithub avatar Mar 26 '24 15:03 dlangBugzillaToGithub

ibuclaw (@ibuclaw) commented on 2024-03-26T15:14:22Z

(In reply to Manu from comment #2)
> That would tell you if you have 64bit address space. Not the same thing.
```
import gcc.builtins : ___builtin_machine_int;
static if (__builtin_machine_int.sizeof == 8):
```

dlangBugzillaToGithub avatar Mar 26 '24 15:03 dlangBugzillaToGithub

bugzilla (@WalterBright) commented on 2024-03-27T00:51:57Z

https://github.com/dlang/dlang.org/pull/3790

dlangBugzillaToGithub avatar Mar 27 '24 00:03 dlangBugzillaToGithub

turkeyman commented on 2024-03-27T02:03:13Z

This issue is strictly about the arch, and pointer width/ABI matters is explicitly NOT what this request is about.

The thing Iain shows:
```
import gcc.builtins : ___builtin_machine_int;
static if (__builtin_machine_int.sizeof == 8):
```

This is what should be expressed by a standard version token present for all compilers/architectures.


On the tangent regarding X32; it is deprecated for linux, but cross compilers will need to express this concept for all time.
For my money, D_X32 should continue to exist, and the spec adjusted to require this constant for all targets where 32bit pointers are used on a 64bit arch, which would include the X32 ABI for X86, but also similar ABI's for other architectures too.
It's not strictly necessary, because we can test size_t.sizeof, but since it's already there and removing it would be a breaking change, the spec could be slightly adjusted to make it generally useful.

dlangBugzillaToGithub avatar Mar 27 '24 02:03 dlangBugzillaToGithub

I have several questions.

  • Would this version be defined if the CPU is capable of 128-bit arithmetic?
  • Floating point or integer arithmetic? Both?
  • What operations are required to be supported in order to qualify?
  • Do the required operations need to have explicit ISA support or do fast equivalents apply? (ex: if subtraction is required, can a fast add+negate qualify?)
  • Would there be 32-bit and 0-bit versions defined in addition to this?
  • What sort of improvements can this actually bring to the code that benefits from it?

Herringway avatar Apr 26 '25 15:04 Herringway

I have several questions.

  • Would this version be defined if the CPU is capable of 128-bit arithmetic?

I would.

  • Floating point or integer arithmetic? Both?

Integers. Normally float width follows; any exceptions (I don't know any) can be determined with an additional test.

  • What operations are required to be supported in order to qualify?

There's no 64bit arch that can't do basic arithmetic... riscv can't do mul/div though, and they need to be implemented for 32/64 bit separately.

  • Do the required operations need to have explicit ISA support or do fast equivalents apply? (ex: if subtraction is required, can a fast add+negate qualify?)

Overcomplicating it, similar to float; if an arch is sufficiently odd, it will always find itself hooked specifically.

  • Would there be 32-bit and 0-bit versions defined in addition to this?

Doesn't seem useful. 128bit though...

  • What sort of improvements can this actually bring to the code that benefits from it?

Can write proper code in the proper cases...

A 32 bit implementation of a 128bit mul/div is way different than a 64bit implementation, etc.

This shouldn't be controversial. If a computer is 32bit or 64bit is a fundamental architectural detail. We need to know...

TurkeyMan avatar Apr 26 '25 16:04 TurkeyMan

I have several questions.

  • Would this version be defined if the CPU is capable of 128-bit arithmetic?

I would.

  • Floating point or integer arithmetic? Both?

Integers. Normally float width follows; any exceptions (I don't know any) can be determined with an additional test.

  • What operations are required to be supported in order to qualify?

There's no 64bit arch that can't do basic arithmetic... riscv can't do mul/div though, and they need to be implemented for 32/64 bit separately.

Defining what "basic arithmetic" refers to is important.

  • Would there be 32-bit and 0-bit versions defined in addition to this?

Doesn't seem useful. 128bit though...

  • What sort of improvements can this actually bring to the code that benefits from it?

Can write proper code in the proper cases...

A 32 bit implementation of a 128bit mul/div is way different than a 64bit implementation, etc.

It would be useful to distinguish between a CPU supporting 32-bit basic arithmetic and one that doesn't implement it at all, then.

This shouldn't be controversial. If a computer is 32bit or 64bit is a fundamental architectural detail. We need to know...

Usually an unqualified 32/64 bit refers to a CPU's word size or address size, which isn't necessarily a native arithmetic type. When it comes to arithmetic, several architectures can be said to be all multiples of 8 bit below its word size (for example, x86-64 is capable of native 8 bit, 16 bit, 32 bit and 64 bit arithmetic simultaneously). This isn't controversial, it's just very complicated and not something that can be distilled into a single number.

Fun fact: Tooling exists to compile D code to run on a 6502...

Herringway avatar Apr 26 '25 17:04 Herringway

I have several questions.

  • Would this version be defined if the CPU is capable of 128-bit arithmetic?

I would.

  • Floating point or integer arithmetic? Both?

Integers. Normally float width follows; any exceptions (I don't know any) can be determined with an additional test.

  • What operations are required to be supported in order to qualify?

There's no 64bit arch that can't do basic arithmetic... riscv can't do mul/div though, and they need to be implemented for 32/64 bit separately.

Defining what "basic arithmetic" refers to is important.

I consider that an appropriate definition.

  • Would there be 32-bit and 0-bit versions defined in addition to this?

Doesn't seem useful. 128bit though...

  • What sort of improvements can this actually bring to the code that benefits from it?

Can write proper code in the proper cases...

A 32 bit implementation of a 128bit mul/div is way different than a 64bit implementation, etc.

It would be useful to distinguish between a CPU supporting 32-bit basic arithmetic and one that doesn't implement it at all, then.

This shouldn't be controversial. If a computer is 32bit or 64bit is a fundamental architectural detail. We need to know...

Usually an unqualified 32/64 bit refers to a CPU's word size or address size, which isn't necessarily a native arithmetic type.

No architecture has expanded the hardware to 64bits and not implemented 64bit arithmetic. That literally makes no sense; why waste the transistors?

When it comes to arithmetic, several architectures can be said to be all multiples of 8 bit below its word size (for example, x86-64 is capable of native 8 bit, 16 bit, 32 bit and 64 bit arithmetic simultaneously). This isn't controversial, it's just very complicated and not something that can be distilled into a single number.

Not useful information; every compiler generates sensible code for all x86 arithmetic, with the one exception that for 32bit targets, special handling is required to implement/emulate 64bit operations not natively available to the architecture.

Fun fact: Tooling exists to compile D code to run on a 6502...

As I said, sufficiently 'odd' architectures will always be special cased specifically. Nobody is writing 8/16bit software; if they are, it's a toy, and the whole point is to emulate the high level stuff on such a system. That's a kinky fetish, which is fine, but not relevant here.

TurkeyMan avatar Apr 27 '25 00:04 TurkeyMan

Fun fact: Tooling exists to compile D code to run on a 6502...

As I said, sufficiently 'odd' architectures will always be special cased specifically. Nobody is writing 8/16bit software; if they are, it's a toy, and the whole point is to emulate the high level stuff on such a system. That's a kinky fetish, which is fine, but not relevant here.

Do you have any idea how inappropriate this is?

Herringway avatar Apr 27 '25 05:04 Herringway

Ummm... I'm gonna say no? What are you talking about?

TurkeyMan avatar Apr 27 '25 07:04 TurkeyMan