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

NativeAOT-LLVM: cast unsigned ints to floats using uitofp #1994

Merged
merged 6 commits into from
Oct 1, 2022

Conversation

yowl
Copy link
Contributor

@yowl yowl commented Sep 26, 2022

This PR fixes a bug when casting ints to floating points. It assumed signed. If the source is an unsigned int, then make the cast unsigned (https://llvm.org/docs/LangRef.html#uitofp-to-instruction) .

Adds a test using a byte > 0x7f

cc @SingleAccretion

@jkotas
Copy link
Member

jkotas commented Sep 28, 2022

@SingleAccretion Could you please take a look at this change? I guess it might have slipped through your inbox.

@@ -4035,7 +4037,15 @@ private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool
}
else
{
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(), wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */);
TypeDesc sourceType = value.Type;
Copy link

@SingleAccretion SingleAccretion Sep 28, 2022

Choose a reason for hiding this comment

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

Who calls ImportConvert?

Edit: I see, it is called from the shared logic.

Comment on lines 4042 to 4048
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(),
wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */
|| sourceType.IsWellKnownType(WellKnownType.Byte)
|| sourceType.IsWellKnownType(WellKnownType.UInt16)
|| sourceType.IsWellKnownType(WellKnownType.UInt32)
|| sourceType.IsWellKnownType(WellKnownType.UInt64)
);

Choose a reason for hiding this comment

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

How does the ILImporter stack work -- does it keep the precise types?

If it does, it does not look correct to look at them. E. g.

ldobj int64
conv.r.un

Should still be treated as an unsigned cast.

/* unsigned is always false, so check for the type explicitly */

Is that true? I see it should be true for conv.r.un.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, no that's not true, let me try to clean this up.

Copy link
Contributor Author

@yowl yowl Sep 28, 2022

Choose a reason for hiding this comment

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

There were 2 reported problems, using a byte as an index and casting a byte to a float (for an alpha channel). But looking at the code I don't see why I can't just pass the right value from the op code.

Comment on lines 4042 to 4048
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(),
wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */
|| sourceType.IsWellKnownType(WellKnownType.Byte)
|| sourceType.IsWellKnownType(WellKnownType.UInt16)
|| sourceType.IsWellKnownType(WellKnownType.UInt32)
|| sourceType.IsWellKnownType(WellKnownType.UInt64)
);
Copy link

@SingleAccretion SingleAccretion Sep 29, 2022

Choose a reason for hiding this comment

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

Suggested change
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(),
wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */
|| sourceType.IsWellKnownType(WellKnownType.Byte)
|| sourceType.IsWellKnownType(WellKnownType.UInt16)
|| sourceType.IsWellKnownType(WellKnownType.UInt32)
|| sourceType.IsWellKnownType(WellKnownType.UInt64)
);
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(), unsigned);

Presumably that should now work?

I also see there is:

            else if ((toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind || toStoreKind == LLVMTypeKind.LLVMFloatTypeKind) &&
                valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind)
            {
                //TODO: keep track of the TypeDesc so we can call BuildFPToUI when the integer is unsigned
                typedToStore = builder.BuildFPToSI(source, valueType, "CastFloatSI" + (name ?? ""));
            }

In CastIfNecessary that, could, presumably also be fixed.

@yowl
Copy link
Contributor Author

yowl commented Sep 30, 2022

I've put code to widen the bytes/shorts to ints before the conv. Added another test. Hopefully this fits the IL semantics better.

Example LLVM is now

  store i8 -1, i8* %alpha_local0_, align 1, !dbg !389570
  %Loadloc0_ = load i8, i8* %alpha_local0_, align 1, !dbg !389571
  %CastZIntloc0_ = zext i8 %Loadloc0_ to i32, !dbg !389571
  %CastSIToFloatloc0_ = sitofp i32 %CastZIntloc0_ to float, !dbg !389571
  %fpextop2 = fpext float %CastSIToFloatloc0_ to double, !dbg !38957

@yowl
Copy link
Contributor Author

yowl commented Sep 30, 2022

ugg, I've messed up the branches somehow... moving to WIP

@yowl yowl marked this pull request as draft September 30, 2022 17:14
@yowl yowl force-pushed the unsigned-to-float branch from b1d8400 to 9b11458 Compare September 30, 2022 17:17
@yowl yowl marked this pull request as ready for review September 30, 2022 17:21
Copy link

@SingleAccretion SingleAccretion left a comment

Choose a reason for hiding this comment

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

LGTM modulo one comment.

TypeDesc destType = GetWellKnownType(wellKnownType);

// Load the value and then convert it instead of using ValueAsType to avoid loading the incorrect size
LLVMValueRef loadedValue = value.ValueAsType(value.Type, _builder);
LLVMValueRef widenedStackValue = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(WidenBytesAndShorts(value.Type)), value.Name(),
stackType.IsWellKnownType(WellKnownType.Byte) ||

Choose a reason for hiding this comment

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

Missing Bool?

@jkotas
Copy link
Member

jkotas commented Oct 1, 2022

@yowl @SingleAccretion Thank you!

@jkotas jkotas merged commit 6629e07 into dotnet:feature/NativeAOT-LLVM Oct 1, 2022
@yowl yowl deleted the unsigned-to-float branch October 1, 2022 18:24
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