Skip to content

Conversation

ashay
Copy link

@ashay ashay commented Aug 16, 2025

Prior to this patch, any negative constants smaller than -0xFFFFFF would
get serialized to 64-bit constants, so we would end up with assembly
statements like imul eax, esi, 0xFFFFFFFFFEFFFFFF, which is invalid
since the constant is larger than 32 bits, which is the size of eax
and esi registers.

This patch fixes the code so that for known 1-, 2, 4-, and 8-byte types,
we first truncate the constant to the correct number of bits before
printing it.

Fix #118813

Prior to this patch, any negative constants smaller than -0xFFFFFF would
get serialized to 64-bit constants, so we would end up with assembly
statements like `imul eax, esi, 0xFFFFFFFFFEFFFFFF`, which is invalid
since the constant is larger than 32 bits, which is the size of `eax`
and `esi` registers.

This patch fixes the code so that for known 1-, 2, 4-, and 8-byte types,
we first truncate the constant to the correct number of bits before
printing it.

Fix dotnet#118813
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Aug 16, 2025
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Aug 16, 2025
@vcsjones vcsjones added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Aug 17, 2025
Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

break;

case EA_8BYTE:
printf("0x%X", static_cast<int64_t>(val));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct if int is 32-bit. %X expects an unsigned int argument but int64_t may be wider than that.

I think you can use "0x%zX" with (size_t)(uint64_t)val. I don't see why one would use the signed type ssize_t with %zX.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that! I had missed that %X expects an unsigned integer. Instead of printing the integers with the sign, I unified the logic for printing both positive and negative numbers by printing the unsigned equivalent. This has the added benefit of making the code simpler, IMO.

ashay added 2 commits August 18, 2025 13:31
The `%X` format specifier expects an unsigned value, so passing a
negaive integer is undefined behavior.  Instead of printing a value with
the sign, this patch prints the two's complement of the negative value
with the appropriate width (as specified in the instruction).  Since
this logic is the same for both positive and negative integers, this
patch unifies the code for printing all integers.
{
printf("%d", (int)val);
Copy link
Contributor

@xtqqczze xtqqczze Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we were printing decimal here not hex

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yikes! Good catch. Fixed.

@Copilot Copilot AI review requested due to automatic review settings August 19, 2025 03:23
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a code generation issue where negative constants were being serialized incorrectly in x86/x64 assembly output. The problem occurred when negative constants smaller than -0xFFFFFF were printed as 64-bit values even when used with smaller operand sizes, resulting in invalid assembly like imul eax, esi, 0xFFFFFFFFFEFFFFFF.

Key changes:

  • Replaced the previous logic that handled positive/negative constants differently with operand size-aware formatting
  • Added a switch statement to truncate constants to the appropriate bit width (1, 2, 4, or 8 bytes) before printing
  • Ensures generated assembly uses correctly sized constants that match the operand types

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

break;

case EA_8BYTE:
printf("0x%X", static_cast<uint64_t>(val));
Copy link
Preview

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format specifier %X is used for uint64_t but should be %llX or %lX depending on the platform. Using %X with a 64-bit value may cause truncation or undefined behavior on some platforms.

Suggested change
printf("0x%X", static_cast<uint64_t>(val));
printf("0x%llX", static_cast<unsigned long long>(val));

Copilot uses AI. Check for mistakes.

break;

default:
printf("0x%zX", static_cast<size_t>(val));
Copy link
Preview

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting a potentially negative ssize_t value to size_t (unsigned) may not preserve the intended bit pattern. Consider using the original (ssize_t)val cast to maintain sign information, or ensure the truncation behavior is intentional.

Suggested change
printf("0x%zX", static_cast<size_t>(val));
printf("0x%zX", static_cast<ssize_t>(val));

Copilot uses AI. Check for mistakes.

}
else
{
// (val < 0)
printf("-0x%zX", (ssize_t)-val);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The below code will never print negative values anymore. I do not think we want that to change.

FWIW, the jit disasm is a power user scenario for curious users only. It is not meant to be full fidelity/correct assembly output, and in various cases the produced disassembly will not be fully faithful. That is expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Overflowing negative constants in printed x64 assembly
5 participants