Skip to content

Commit

Permalink
Additional intrinsics for the NativePtr module (#11682)
Browse files Browse the repository at this point in the history
* Update nativeptr.fs

* Complete signatures

* Test

* Fix comment

* Update NativeInterop.fs

* Improve wordings

* Styling

* More styling

* Group conversion functions together

* Consistent wording again

* Improve test

* Note on ilsigptr

* Oops voidptr is okay too

* Update nativeptr.fsi

* Update prim-types-prelude.fsi

* Update prim-types-prelude.fsi

* Capitalize two letter acronyms

* Done
  • Loading branch information
Happypig375 authored Jul 13, 2021
1 parent b4ba6f7 commit 6dd7131
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 48 deletions.
57 changes: 44 additions & 13 deletions src/fsharp/FSharp.Core/nativeptr.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,78 @@ open System.Runtime.InteropServices
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module NativePtr =


[<NoDynamicInvocation>]
[<CompiledName("OfNativeIntInlined")>]
let inline ofNativeInt (address:nativeint) = (# "" address : nativeptr<'T> #)
let inline ofNativeInt (address: nativeint) = (# "" address : nativeptr<'T> #)

[<NoDynamicInvocation>]
[<CompiledName("ToNativeIntInlined")>]
let inline toNativeInt (address: nativeptr<'T>) = (# "" address : nativeint #)
let inline toNativeInt (address: nativeptr<'T>) = (# "" address : nativeint #)

[<NoDynamicInvocation>]
[<CompiledName("OfVoidPtrInlined")>]
let inline ofVoidPtr (address: voidptr) = (# "" address : nativeptr<'T> #)

[<NoDynamicInvocation>]
[<CompiledName("ToVoidPtrInlined")>]
let inline toVoidPtr (address: nativeptr<'T>) = (# "" address : voidptr #)

[<NoDynamicInvocation>]
[<CompiledName("OfVoidPtrInlined")>]
let inline ofVoidPtr (address: voidptr) = (# "" address : nativeptr<'T> #)
[<CompiledName("OfILSigPtrInlined")>]
let inline ofILSigPtr (address: ilsigptr<'T>) = (# "" address : nativeptr<'T> #)

[<NoDynamicInvocation>]
[<CompiledName("ToILSigPtrInlined")>]
let inline toILSigPtr (address: nativeptr<'T>) = (# "" address : ilsigptr<'T> #)

[<NoDynamicInvocation>]
[<CompiledName("ToByRefInlined")>]
let inline toByRef (address: nativeptr<'T>) : byref<'T> = (# "" address : 'T byref #)

[<NoDynamicInvocation>]
[<CompiledName("AddPointerInlined")>]
let inline add (address : nativeptr<'T>) (index:int) : nativeptr<'T> = toNativeInt address + nativeint index * (# "sizeof !0" type('T) : nativeint #) |> ofNativeInt
let inline add (address: nativeptr<'T>) (index: int) : nativeptr<'T> = toNativeInt address + nativeint index * (# "sizeof !0" type('T) : nativeint #) |> ofNativeInt

[<NoDynamicInvocation>]
[<CompiledName("GetPointerInlined")>]
let inline get (address : nativeptr<'T>) index = (# "ldobj !0" type ('T) (add address index) : 'T #)
let inline get (address: nativeptr<'T>) index = (# "ldobj !0" type('T) (add address index) : 'T #)

[<NoDynamicInvocation>]
[<CompiledName("SetPointerInlined")>]
let inline set (address : nativeptr<'T>) index (value : 'T) = (# "stobj !0" type ('T) (add address index) value #)
let inline set (address: nativeptr<'T>) index (value: 'T) = (# "stobj !0" type('T) (add address index) value #)

[<NoDynamicInvocation>]
[<CompiledName("ReadPointerInlined")>]
let inline read (address : nativeptr<'T>) = (# "ldobj !0" type ('T) address : 'T #)
let inline read (address: nativeptr<'T>) = (# "ldobj !0" type('T) address : 'T #)

[<NoDynamicInvocation>]
[<CompiledName("WritePointerInlined")>]
let inline write (address : nativeptr<'T>) (value : 'T) = (# "stobj !0" type ('T) address value #)
let inline write (address: nativeptr<'T>) (value : 'T) = (# "stobj !0" type('T) address value #)

[<NoDynamicInvocation>]
[<CompiledName("StackAllocate")>]
let inline stackalloc (count:int) : nativeptr<'T> = (# "localloc" (count * sizeof<'T>) : nativeptr<'T> #)
let inline stackalloc (count: int) : nativeptr<'T> = (# "localloc" (count * sizeof<'T>) : nativeptr<'T> #)

[<NoDynamicInvocation>]
[<CompiledName("NullPointer")>]
let inline nullPtr<'T when 'T : unmanaged> : nativeptr<'T> = (# "ldnull" : nativeptr<'T> #)

[<NoDynamicInvocation>]
[<CompiledName("IsNullPointer")>]
let inline isNullPtr (address: nativeptr<'T>) = (# "ceq" nullPtr<'T> address : bool #)

[<NoDynamicInvocation>]
[<CompiledName("ClearPointerInlined")>]
let inline clear (address: nativeptr<'T>) = (# "initobj !0" type('T) address #)

[<NoDynamicInvocation>]
[<CompiledName("ToByRefInlined")>]
let inline toByRef (address: nativeptr<'T>) : byref<'T> = (# "" address : 'T byref #)
[<CompiledName("InitializeBlockInlined")>]
let inline initBlock (address: nativeptr<'T>) (value: byte) (count: uint32) = (# "initblk" address value count #)

[<NoDynamicInvocation>]
[<CompiledName("CopyPointerInlined")>]
let inline copy (destination: nativeptr<'T>) (source: nativeptr<'T>) = (# "cpobj !0" type('T) destination source #)

[<NoDynamicInvocation>]
[<CompiledName("CopyBlockInlined")>]
let inline copyBlock (destination: nativeptr<'T>) (source: nativeptr<'T>) (count: int) = (# "cpblk" destination source (count * sizeof<'T>) #)
118 changes: 93 additions & 25 deletions src/fsharp/FSharp.Core/nativeptr.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,65 @@ namespace Microsoft.FSharp.NativeInterop
[<Unverifiable>]
[<CompiledName("OfNativeIntInlined")>]
/// <summary>Returns a typed native pointer for a given machine address.</summary>
/// <param name="address">The pointer address.</param>
///
/// <returns>A typed pointer.</returns>
val inline ofNativeInt : address:nativeint -> nativeptr<'T>
/// <param name="address">The machine address.</param>
///
/// <returns>A typed native pointer.</returns>
val inline ofNativeInt : address: nativeint -> nativeptr<'T>

[<Unverifiable>]
[<CompiledName("ToVoidPtrInlined")>]
/// <summary>Returns an untyped native pointer for a given typed pointer.</summary>
[<CompiledName("ToNativeIntInlined")>]
/// <summary>Returns a machine address for a given typed native pointer.</summary>
///
/// <param name="address">The pointer address.</param>
/// <param name="address">The typed native pointer.</param>
///
/// <returns>A typed pointer.</returns>
val inline toVoidPtr : address:nativeptr<'T> -> voidptr
/// <returns>The machine address.</returns>
val inline toNativeInt : address: nativeptr<'T> -> nativeint

[<Unverifiable>]
[<CompiledName("OfVoidPtrInlined")>]
/// <summary>Returns a typed native pointer for a untyped native pointer.</summary>
///
/// <param name="address">The untyped pointer.</param>
/// <param name="address">The untyped native pointer.</param>
///
/// <returns>A typed pointer.</returns>
/// <returns>A typed native pointer.</returns>
val inline ofVoidPtr : address: voidptr -> nativeptr<'T>

[<Unverifiable>]
[<CompiledName("ToNativeIntInlined")>]
/// <summary>Returns a machine address for a given typed native pointer.</summary>
[<CompiledName("ToVoidPtrInlined")>]
/// <summary>Returns an untyped native pointer for a given typed native pointer.</summary>
///
/// <param name="address">The input pointer.</param>
/// <param name="address">The typed native pointer.</param>
///
/// <returns>The machine address.</returns>
val inline toNativeInt : address:nativeptr<'T> -> nativeint
/// <returns>An untyped native pointer.</returns>
val inline toVoidPtr : address: nativeptr<'T> -> voidptr

[<Unverifiable>]
[<CompiledName("OfILSigPtrInlined")>]
/// <summary>Returns a typed native pointer for a Common IL (Intermediate Language) signature pointer.</summary>
///
/// <param name="address">The Common IL signature pointer.</param>
///
/// <returns>A typed native pointer.</returns>
val inline ofILSigPtr : address: ilsigptr<'T> -> nativeptr<'T>

[<Unverifiable>]
[<CompiledName("ToILSigPtrInlined")>]
/// <summary>Returns a Common IL (Intermediate Language) signature pointer for a given typed native pointer.</summary>
///
/// <param name="address">The typed native pointer.</param>
///
/// <returns>A Common IL signature pointer.</returns>
val inline toILSigPtr : address: nativeptr<'T> -> ilsigptr<'T>

/// <summary>Converts a given typed native pointer to a managed pointer.</summary>
///
/// <param name="address">The typed native pointer.</param>
///
/// <returns>The managed pointer.</returns>
[<Unverifiable>]
[<CompiledName("ToByRefInlined")>]
val inline toByRef: address: nativeptr<'T> -> byref<'T>

[<Unverifiable>]
[<CompiledName("AddPointerInlined")>]
Expand All @@ -62,7 +89,7 @@ namespace Microsoft.FSharp.NativeInterop
/// <param name="index">The index by which to offset the pointer.</param>
///
/// <returns>A typed pointer.</returns>
val inline add : address:nativeptr<'T> -> index:int -> nativeptr<'T>
val inline add : address: nativeptr<'T> -> index: int -> nativeptr<'T>

[<Unverifiable>]
[<CompiledName("GetPointerInlined")>]
Expand All @@ -73,7 +100,7 @@ namespace Microsoft.FSharp.NativeInterop
/// <param name="index">The index by which to offset the pointer.</param>
///
/// <returns>The value at the pointer address.</returns>
val inline get : address:nativeptr<'T> -> index:int -> 'T
val inline get : address: nativeptr<'T> -> index: int -> 'T

[<Unverifiable>]
[<CompiledName("ReadPointerInlined")>]
Expand All @@ -82,15 +109,15 @@ namespace Microsoft.FSharp.NativeInterop
/// <param name="address">The input pointer.</param>
///
/// <returns>The value at the pointer address.</returns>
val inline read : address:nativeptr<'T> -> 'T
val inline read : address: nativeptr<'T> -> 'T

[<Unverifiable>]
[<CompiledName("WritePointerInlined")>]
/// <summary>Assigns the <c>value</c> into the memory location referenced by the given typed native pointer.</summary>
///
/// <param name="address">The input pointer.</param>
/// <param name="value">The value to assign.</param>
val inline write : address:nativeptr<'T> -> value:'T -> unit
val inline write : address: nativeptr<'T> -> value: 'T -> unit

[<Unverifiable>]
[<CompiledName("SetPointerInlined")>]
Expand All @@ -100,7 +127,7 @@ namespace Microsoft.FSharp.NativeInterop
/// <param name="address">The input pointer.</param>
/// <param name="index">The index by which to offset the pointer.</param>
/// <param name="value">The value to assign.</param>
val inline set : address:nativeptr<'T> -> index:int -> value:'T -> unit
val inline set : address: nativeptr<'T> -> index: int -> value: 'T -> unit

/// <summary>Allocates a region of memory on the stack.</summary>
///
Expand All @@ -109,13 +136,54 @@ namespace Microsoft.FSharp.NativeInterop
/// <returns>A typed pointer to the allocated memory.</returns>
[<Unverifiable>]
[<CompiledName("StackAllocate")>]
val inline stackalloc: count:int -> nativeptr<'T>
val inline stackalloc: count: int -> nativeptr<'T>

/// <summary>Gets the null native pointer.</summary>
///
/// <returns>The null native pointer.</returns>
[<Unverifiable>]
[<GeneralizableValue>]
[<CompiledName("NullPointer")>]
val inline nullPtr<'T when 'T : unmanaged> : nativeptr<'T>

/// <summary>Tests whether the given native pointer is null.</summary>
///
/// <param name="address">The input pointer.</param>
///
/// <returns>Whether the given native pointer is null.</returns>
[<Unverifiable>]
[<CompiledName("IsNullPointer")>]
val inline isNullPtr: address: nativeptr<'T> -> bool

[<Unverifiable>]
[<CompiledName("ClearPointerInlined")>]
/// <summary>Clears the value stored at the location of a given native pointer.</summary>
///
/// <param name="address">The input pointer.</param>
val inline clear : address: nativeptr<'T> -> unit

/// <summary>Converts a given typed native pointer to a managed pointer.</summary>
[<Unverifiable>]
[<CompiledName("InitializeBlockInlined")>]
/// <summary>Initializes a specified block of memory starting at a specific address to a given byte count and initial byte value.</summary>
///
/// <param name="address">The input pointer.</param>
/// <param name="value">The initial byte value.</param>
/// <param name="count">The total repeat count of the byte value.</param>
val inline initBlock : address: nativeptr<'T> -> value: byte -> count: uint32 -> unit

[<Unverifiable>]
[<CompiledName("CopyPointerInlined")>]
/// <summary>Copies a value to a specified destination address from a specified source address.</summary>
///
/// <returns>The managed pointer.</returns>
/// <param name="destination">The destination pointer.</param>
/// <param name="source">The source pointer.</param>
val inline copy : destination: nativeptr<'T> -> source: nativeptr<'T> -> unit

[<Unverifiable>]
[<CompiledName("ToByRefInlined")>]
val inline toByRef: address: nativeptr<'T> -> byref<'T>
[<CompiledName("CopyBlockInlined")>]
/// <summary>Copies a block of memory to a specified destination address starting from a specified source address until a specified byte count of (count * sizeof&lt;'T&gt;).</summary>
///
/// <param name="destination">The destination pointer.</param>
/// <param name="source">The source pointer.</param>
/// <param name="count">The source pointer.</param>
val inline copyBlock : destination: nativeptr<'T> -> source: nativeptr<'T> -> count: int -> unit
23 changes: 18 additions & 5 deletions src/fsharp/FSharp.Core/prim-types-prelude.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,8 @@ namespace Microsoft.FSharp.Core
/// <summary>Represents an unmanaged pointer in F# code.</summary>
///
/// <remarks>This type should only be used when writing F# code that interoperates
/// with native code. Use of this type in F# code may result in
/// unverifiable code being generated. Conversions to and from the
/// with native code. Use of this type in F# code may result in
/// unverifiable code being generated. Conversions to and from the
/// <see cref="T:Microsoft.FSharp.Core.nativeint" /> type may be required. Values of this type can be generated
/// by the functions in the <c>NativeInterop.NativePtr</c> module.</remarks>
///
Expand All @@ -415,15 +415,28 @@ namespace Microsoft.FSharp.Core
/// <summary>Represents an untyped unmanaged pointer in F# code.</summary>
///
/// <remarks>This type should only be used when writing F# code that interoperates
/// with native code. Use of this type in F# code may result in
/// unverifiable code being generated. Conversions to and from the
/// with native code. Use of this type in F# code may result in
/// unverifiable code being generated. Conversions to and from the
/// <see cref="T:Microsoft.FSharp.Core.nativeint" /> type may be required. Values of this type can be generated
/// by the functions in the <c>NativeInterop.NativePtr</c> module.</remarks>
///
/// <category>ByRef and Pointer Types</category>
type voidptr = (# "void*" #)

/// <summary>This type is for internal use by the F# code generator.</summary>
/// <summary>Represents an Common IL (Intermediate Language) Signature Pointer.</summary>
///
/// <remarks>This type should only be used when writing F# code that interoperates
/// with other .NET languages that use generic Common IL Signature Pointers.
/// Use of this type in F# code may result in unverifiable code being generated.
/// Because of the rules of Common IL Signature Pointers, you cannot use this type in generic type parameters,
/// resulting in compiler errors. As a result, you should convert this type to <see cref="T:Microsoft.FSharp.Core.nativeptr{T}" />
/// for use in F#. Note that Common IL Signature Pointers exposed by other .NET languages are converted to
/// <see cref="T:Microsoft.FSharp.Core.nativeptr{T}" /> or <see cref="T:Microsoft.FSharp.Core.voidptr" /> automatically by F#,
/// and F# also shows generic-specialized typed native pointers correctly to other .NET languages as Common IL Signature Pointers.
/// However, generic typed native pointers are shown as <see cref="T:System.IntPtr"/> to other .NET languages.
/// For other languages to interpret generic F# typed native pointers correctly, you should expose this type or
/// <see cref="T:Microsoft.FSharp.Core.voidptr" /> instead of <see cref="T:Microsoft.FSharp.Core.nativeptr{T}" />.
/// Values of this type can be generated by the functions in the <c>NativeInterop.NativePtr</c> module.</remarks>
///
/// <category>ByRef and Pointer Types</category>
type ilsigptr<'T> = (# "!0*" #)
Expand Down
1 change: 0 additions & 1 deletion src/fsharp/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4011,7 +4011,6 @@ and GenAsmCode cenv cgbuf eenv (il, tyargs, args, returnTys, m) sequel =
| I_stobj (a, b, ILType.TypeVar _), [tyarg] -> I_stobj (a, b, tyarg)
| I_ldtoken (ILToken.ILType (ILType.TypeVar _)), [tyarg] -> I_ldtoken (ILToken.ILType tyarg)
| I_sizeof (ILType.TypeVar _), [tyarg] -> I_sizeof tyarg
// currently unused, added for forward compat, see https://visualfsharp.codeplex.com/SourceControl/network/forks/jackpappas/fsharpcontrib/contribution/7134
| I_cpobj (ILType.TypeVar _), [tyarg] -> I_cpobj tyarg
| I_initobj (ILType.TypeVar _), [tyarg] -> I_initobj tyarg
| I_ldfld (al, vol, fspec), _ -> I_ldfld (al, vol, modFieldSpec fspec)
Expand Down
8 changes: 4 additions & 4 deletions src/fsharp/TypedTreePickle.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,10 +1195,10 @@ let [<Literal>] itag_ldelem_any = 59
let [<Literal>] itag_stelem_any = 60
let [<Literal>] itag_unbox_any = 61
let [<Literal>] itag_ldlen_multi = 62
let [<Literal>] itag_initobj = 63 // currently unused, added for forward compat, see https://visualfsharp.codeplex.com/SourceControl/network/forks/jackpappas/fsharpcontrib/contribution/7134
let [<Literal>] itag_initblk = 64 // currently unused, added for forward compat
let [<Literal>] itag_cpobj = 65 // currently unused, added for forward compat
let [<Literal>] itag_cpblk = 66 // currently unused, added for forward compat
let [<Literal>] itag_initobj = 63
let [<Literal>] itag_initblk = 64
let [<Literal>] itag_cpobj = 65
let [<Literal>] itag_cpblk = 66

let simple_instrs =
[ itag_add, AI_add
Expand Down
1 change: 1 addition & 0 deletions tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<Compile Include="FSharp.Core\OperatorsModule1.fs" />
<Compile Include="FSharp.Core\OperatorsModule2.fs" />
<Compile Include="FSharp.Core\OperatorsModuleChecked.fs" />
<Compile Include="FSharp.Core\NativeInterop.fs" />

<Compile Include="FSharp.Core\Microsoft.FSharp.Collections\Utils.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Collections\ArrayModule.fs" />
Expand Down
Loading

0 comments on commit 6dd7131

Please sign in to comment.