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

proposal: cmd/asm: make GOARM available as a #define in the ARM assembler #22106

Closed
ncw opened this issue Oct 2, 2017 · 10 comments
Closed

proposal: cmd/asm: make GOARM available as a #define in the ARM assembler #22106

ncw opened this issue Oct 2, 2017 · 10 comments

Comments

@ncw
Copy link
Contributor

ncw commented Oct 2, 2017

As part of #4299 and change 38366 in particular it would be really useful to have the value of GOARM available to the assembler as a #define-ed label. This was suggested in the review by @FiloSottile .

This would then make it possible to specialize the code for different ARM processors. For example the crypto code in OpenSSL asserts that ARMv7 can do non aligned word loads, whereas this is not possible for ARMv6. Non aligned word loads are much faster where available.

@ianlancetaylor ianlancetaylor added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Oct 3, 2017
@ianlancetaylor ianlancetaylor added this to the Go1.10 milestone Oct 3, 2017
@rsc
Copy link
Contributor

rsc commented Oct 23, 2017

Obviously this should not be a one-off for GOARM but would also apply to GO386, GOMIPS, and so on. This is more complicated by the plans to make at least GOMIPS be a comma-separated list of things. We've strongly resisted this kind of #ifdef in the past and probably should continue to do so. But /cc @aclements, @cherryyz, and @randall77 for discussion.

It might be that we should document better a set of variables visible from assembly to select a code path (run-time checks), instead of compile-time checks.

@rsc rsc changed the title cmd/asm: Make GOARM available as a #define in the ARM assembler proposal: cmd/asm: make GOARM available as a #define in the ARM assembler Oct 23, 2017
@rsc rsc modified the milestones: Go1.10, Proposal Oct 23, 2017
@randall77
Copy link
Contributor

Maybe not #defines, but build tags? That way you could specialize assembly in different files based on the build tag. We could have a goarm_7 build tag for a GOARM=7 build.

Even if we didn't change the tooling any, perhaps it would help to define a standard set of build tags so that you could build for armv7 with -tags goarm_7.
(A project could do that by itself, of course. But for a project importing several other projects, each which has such customization, a common tag set would make life simpler.)

@FiloSottile
Copy link
Contributor

Since GOARM, GO386, GOMIPS are compile-time, I see no reason to make the checks at run-time. Run-time checks in high-perf crypto code are either expensive (if granular) or require a lot of code duplication (if general). Build tags have the same problem.

https://go-review.googlesource.com/c/go/+/38366 is a good example use-case for the #defines.

@ncw
Copy link
Contributor Author

ncw commented Oct 24, 2017

@FiloSottile wrote:

Since GOARM, GO386, GOMIPS are compile-time, I see no reason to make the checks at run-time. Run-time checks in high-perf crypto code are either expensive (if granular) or require a lot of code duplication (if general). Build tags have the same problem.

I would have to agree with most of that, however something could be done about the code duplication I think.

The assembler supports #include, so when using build tags you could set your own #defines before including the monolithic bit of code with #ifdef in it.

A quick bit of noodling about with the assembler has convinced me that this would work. Any .s file you #include should have a build tag of none.

With a run time check, you could build the code twice by including the file twice with different macros set and indirect the call through a pointer you could set up in your init function. This bloats up the binary and costs (a probably affordable) function indirect.

@aclements
Copy link
Member

Since GOARM, GO386, GOMIPS are compile-time, I see no reason to make the checks at run-time.

I think you're begging the question. A run-time check wouldn't be for the value of GOARM, etc, it would be for what hardware you're actually running on. So, the advantage of a run-time check is that it can use whatever hardware is available even if the binary was built for something more constrained.

If we could eliminate these compile-time options and make the compiled code depend exclusively on run-time checks, we absolutely would. But the consequences of these get sprinkled throughout the generated code, so it's too expensive to do the run-time checks (mostly in terms of performance, maybe in terms of binary size, though the latter is unclear). In a lot of cases, the cost of the run-time check could outweigh the cost of just doing it the slower way even if the faster way is available. Does this sort of reasoning apply to the crypto code?

@ncw
Copy link
Contributor Author

ncw commented Oct 24, 2017

Does this sort of reasoning apply to the crypto code?

The crypto code I've looked at tends to run a goodly chunk of stuff before returning back to go. This means you could specialize it with function pointers without very much overhead in terms of performance, but at a cost of binary size. For ARM you'd probably want an ARMv7 a NEON and an everything else specialization.

@laboger
Copy link
Contributor

laboger commented Oct 25, 2017

We would definitely like to have GOPPC to be able to identify the target instruction set at compile time.

@rsc
Copy link
Contributor

rsc commented Oct 25, 2017

We would definitely like to have GOPPC to be able to identify the target instruction set at compile time.

Can you say why?

@laboger
Copy link
Contributor

laboger commented Oct 25, 2017

So the compiler can generate power9 instructions without having to add runtime checks.

@rsc rsc removed the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Oct 30, 2017
@rsc
Copy link
Contributor

rsc commented Nov 8, 2017

We have a build-time environment variable GO$GOARCH (GO386, GOARM) that sets kind of processor features the toolchain is allowed to assume. For example GO386=sse2 lets the x86-32 compiler assume it can use SSE2-based floating-point instructions instead of resorting to the 387 instructions. That build-time environment variable is available to and consulted by the compiler and the linker. All things considered we'd even rather not have those, but the case was compelling for the changes on newer arm and newer x86-32 chips. If there is something compelling in the ppc64 compiler that would be helped by power9, then we should have a discussion about adding GOPPC64 and GOPPC64LE, but in a different issue (feel free to file one). But that's not this issue.

This issue is about making the existing GOARM setting available to assembly writers for use in #ifdef, or available as a build tag to allow selection of specific files. Those both introduce more significant complexity to the understanding of the code, and to date we've avoided needing to do that. If at all possible, I would like to continue to avoid needing that. To date, we have gotten along fine by writing assembly that checks, at run time, which ARM chip it is running on by consulting runtime.goarm, and jumps to the right code. As long as the operation being implemented is not trivial, the cost of this one check and jump is not significant.

For example:

$ git grep -n runtime·goarm
src/runtime/asm_arm.s:42:	MOVB    runtime·goarm(SB), R11
src/runtime/asm_arm.s:77:	MOVB    runtime·goarm(SB), R11
src/runtime/asm_arm.s:197:	MOVB	runtime·goarm(SB), R11
src/runtime/asm_arm.s:788:	MOVB	runtime·goarm(SB), R11
src/runtime/cgo/asm_arm.s:23:	MOVB    runtime·goarm(SB), R11
src/runtime/cgo/asm_arm.s:40:	MOVB    runtime·goarm(SB), R11
src/runtime/internal/atomic/asm_arm.s:30:	MOVB	runtime·goarm(SB), R11
src/runtime/internal/atomic/asm_arm.s:40:	MOVB	runtime·goarm(SB), R11
src/sync/atomic/asm_arm.s:12:	MOVB	runtime·goarm(SB), R11; \
src/sync/atomic/asm_arm.s:18:	MOVB	runtime·goarm(SB), R11; \
src/sync/atomic/asm_plan9_arm.s:8:	MOVB	runtime·goarm(SB), R11; \
$

Especially for cryptography code, where the assembly is being invoked for block operations, this one movb, test, and conditional jump is just not going to matter. So in the interests of keeping the build system simple and the code more understandable, I would say that if you want to have GOARM=7-specific code, put it behind an appropriate check (and one not in an inner loop, obviously).

There may come a use case where we really need the #ifdef, but AES is not that case. Please use a runtime check in CL 38366.

@rsc rsc closed this as completed Nov 8, 2017
@golang golang locked and limited conversation to collaborators Nov 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants