Skip to content

Commit

Permalink
More nullability changes
Browse files Browse the repository at this point in the history
* Adjusted general philosophy for handling nulls, there are many cases where properties of an object are pulled from an LLVM IR operand. That technically is nullable, however in the context of that type and property it should never be null. In such cases the '!' operator is applied. Thus any such, totally unexpected cases can still trigger a fatal null ref exception but the code is simpler and more efficient by not always checking for null when it isn't ever supposed to be null. (The only way it can be is  bug in the LLVM internals or a bug in the .NET bindings in this library)
* Reversed course on Debug metadata types nullability and null object pattern. Fundamentally LLVM itself is designed where DIScope, DIFile and DIType are allowed null in most cases (and in fact null for each has a meaning. [i.e. DIType == null => void]) Fighting against that complicates the code base and makes using the API harder. The biggest problem is in mapping a handle back to a concrete type. Since handles are equivalent to a pointer to the base type it's impossible to determine what the derived type is supposed to be when the pointer is null. There are a lot of places in the code (especially operands) that deal in collections of the base type and there's no way to determine which "null object" is supposed to be used. Thus it is simpler and cleaner to accept the design of the underlying LLVM library and formally declare these as nullable, so that analysis tooling can warn consumers appropriately. There are cases where a null is not appropriate (like creating an array, where void makes no sense as the element type)
* Significant re-work of the operand handling. The previous approach of using an operand container interface was just plain confusing and hard to follow. (Multiple indexers in play depending on explicit interface vs not etc... made it hard to read and follow - the new approach uses more specific classes that implement a common interface and that supports slicing via the new C#8 ranges so creating slices for properties is much simpler.
  • Loading branch information
smaillet committed Apr 26, 2020
1 parent 5301a24 commit 4eb1ef2
Show file tree
Hide file tree
Showing 67 changed files with 794 additions and 691 deletions.
2 changes: 1 addition & 1 deletion BuildVersion.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
BuildMinor = "0"
BuildPatch = "0"
PreReleaseName = "alpha"
PreReleaseNumber ="2"
PreReleaseNumber ="3"
/>
4 changes: 2 additions & 2 deletions Samples/CodeGenWithDebugInfo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public static void Main( string[ ] args )
module.SourceFileName = Path.GetFileName( srcPath );
module.TargetTriple = TargetDetails.TargetMachine.Triple;
module.Layout = TargetDetails.TargetMachine.TargetData;
System.Diagnostics.Debug.Assert( !( module.DICompileUnit is null ), "Expected module with non-null compile unit" );
Debug.Assert( !( module.DICompileUnit is null ), "Expected module with non-null compile unit" );

TargetDependentAttributes = TargetDetails.BuildTargetDependentFunctionAttributes( context );
#endregion
Expand All @@ -94,7 +94,7 @@ public static void Main( string[ ] args )
// Create basic types used in this compilation
var i32 = new DebugBasicType( module.Context.Int32Type, module, "int", DiTypeKind.Signed );
var f32 = new DebugBasicType( module.Context.FloatType, module, "float", DiTypeKind.Float );
var voidType = DebugType.Create<ITypeRef,DIType>( module.Context.VoidType, DITypeVoid.Instance );
var voidType = DebugType.Create<ITypeRef,DIType>( module.Context.VoidType, null );
var i32Array_0_32 = i32.CreateArrayType( module, 0, 32 );
#endregion

Expand Down
2 changes: 1 addition & 1 deletion Samples/Kaleidoscope/Chapter3/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
}

var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
var retVal = Module.AddFunction( prototype.Name, llvmSignature );
var retVal = Module.CreateFunction( prototype.Name, llvmSignature );

int index = 0;
foreach( var argId in prototype.Parameters )
Expand Down
2 changes: 1 addition & 1 deletion Samples/Kaleidoscope/Chapter4/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
}

var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
var retVal = Module.AddFunction( prototype.Name, llvmSignature );
var retVal = Module.CreateFunction( prototype.Name, llvmSignature );

int index = 0;
foreach( var argId in prototype.Parameters )
Expand Down
2 changes: 1 addition & 1 deletion Samples/Kaleidoscope/Chapter5/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
}

var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
var retVal = Module.AddFunction( prototype.Name, llvmSignature );
var retVal = Module.CreateFunction( prototype.Name, llvmSignature );

int index = 0;
foreach( var argId in prototype.Parameters )
Expand Down
2 changes: 1 addition & 1 deletion Samples/Kaleidoscope/Chapter6/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
}

var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
var retVal = Module.AddFunction( prototype.Name, llvmSignature );
var retVal = Module.CreateFunction( prototype.Name, llvmSignature );

int index = 0;
foreach( var argId in prototype.Parameters )
Expand Down
2 changes: 1 addition & 1 deletion Samples/Kaleidoscope/Chapter7.1/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
}

var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
var retVal = Module.AddFunction( prototype.Name, llvmSignature );
var retVal = Module.CreateFunction( prototype.Name, llvmSignature );

int index = 0;
foreach( var argId in prototype.Parameters )
Expand Down
2 changes: 1 addition & 1 deletion Samples/Kaleidoscope/Chapter7/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
}

var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
var retVal = Module.AddFunction( prototype.Name, llvmSignature );
var retVal = Module.CreateFunction( prototype.Name, llvmSignature );

int index = 0;
foreach( var argId in prototype.Parameters )
Expand Down
6 changes: 3 additions & 3 deletions Samples/Kaleidoscope/Chapter8/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ public OptionalValue<BitcodeModule> Generate( IAstNode ast )

if( AnonymousFunctions.Count > 0 )
{
var mainFunction = Module.AddFunction( "main", Context.GetFunctionType( Context.VoidType ) );
var mainFunction = Module.CreateFunction( "main", Context.GetFunctionType( Context.VoidType ) );
var block = mainFunction.AppendBasicBlock( "entry" );
var irBuilder = new InstructionBuilder( block );
var printdFunc = Module.AddFunction( "printd", Context.GetFunctionType( Context.DoubleType, Context.DoubleType ) );
var printdFunc = Module.CreateFunction( "printd", Context.GetFunctionType( Context.DoubleType, Context.DoubleType ) );
foreach( var anonFunc in AnonymousFunctions )
{
var value = irBuilder.Call( anonFunc );
Expand Down Expand Up @@ -482,7 +482,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
}

var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
var retVal = Module.AddFunction( prototype.Name, llvmSignature );
var retVal = Module.CreateFunction( prototype.Name, llvmSignature );

int index = 0;
foreach( var argId in prototype.Parameters )
Expand Down
6 changes: 3 additions & 3 deletions Samples/Kaleidoscope/Chapter9/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ public OptionalValue<BitcodeModule> Generate( IAstNode ast )

if( AnonymousFunctions.Count > 0 )
{
var mainFunction = Module.AddFunction( "main", Context.GetFunctionType( Context.VoidType ) );
var mainFunction = Module.CreateFunction( "main", Context.GetFunctionType( Context.VoidType ) );
var block = mainFunction.AppendBasicBlock( "entry" );
var irBuilder = new InstructionBuilder( block );
var printdFunc = Module.AddFunction( "printd", Context.GetFunctionType( Context.DoubleType, Context.DoubleType ) );
var printdFunc = Module.CreateFunction( "printd", Context.GetFunctionType( Context.DoubleType, Context.DoubleType ) );
foreach( var anonFunc in AnonymousFunctions )
{
var value = irBuilder.Call( anonFunc );
Expand Down Expand Up @@ -549,7 +549,7 @@ private IrFunction GetOrDeclareFunction( Prototype prototype )
if( prototype.IsExtern )
{
var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) );
retVal = Module.AddFunction( prototype.Name, llvmSignature );
retVal = Module.CreateFunction( prototype.Name, llvmSignature );
}
else
{
Expand Down
5 changes: 3 additions & 2 deletions Ubiquity.NET.ruleset
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@
<Rule Id="IDE0022WithoutSuggestion" Action="None" />
<Rule Id="IDE0034" Action="Warning" />
<Rule Id="IDE0058" Action="None" />
<Rule Id="IDE0067" Action="None"/>
<Rule Id="IDE0067" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.VisualBasic" RuleNamespace="Microsoft.CodeAnalysis.VisualBasic">
<Rule Id="AD0001" Action="None" />
Expand Down Expand Up @@ -735,8 +735,9 @@
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1720" Action="None" />
<Rule Id="CA1062" Action="Error" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="Hidden" />
</Rules>
</RuleSet>
</RuleSet>
9 changes: 7 additions & 2 deletions docfx/current/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ to make nullability more explicit. This necessitated a few minor breaking change
| Name | Description |
|-----------------|--------------|
| DebugMemberInfo | Removed setters of non-nullable properties and added constructor to allow building the type with non-null values |
| DIType | null is no longer used to represent a void type, instead a new singleton DITypeVoid.Instance is used.|


### Renamed instruction predicate enumerator values
The comparison instruction predicates `Ubiquity.NET.Llvm.Instructions.[Predicate|IntPredicate]`were renamed for greater consistency
Expand All @@ -58,14 +58,19 @@ Some APIs had inconsistent, misspelled or confusing names and were updated.
|------------------------|--------------|
| `Ubiquity.NET.Llvm.Transforms.ScalarTransforms.LowerAtomicPass<T>` | `Ubiquity.NET.Llvm.Transforms.ScalarTransforms.AddLowerAtomicPass<T>` |

### Obsoleted APIs
| Obsolete API | Alternative API | Justification |
|---------------------------|--------------------------------|---------------|
| BitcodeModule.AddFunction | BitcodeModule.CreateFunction() | The Create vs Add between debug info and raw native was always confusing |

### Removed redundant APIs
LLVM has made additional APIs available in the standard LLVM-C library that are either identical to or functionality equivalent to
APIs that were custom in previous versions of the Ubiquity.NET.Llvm DLLs. This is only observable at the interop library layer where some
of the custom APIs were removed and replaced with the official ones.

| Removed custom API | New Official API |
|--------------------|------------------|
| LibLLVMFoo{TBD} | LLVMFoo{TBD} |
| LibLLVMFoo [TBD] | LLVMFoo [TBD] |

### Disabled ORCJIT LazyFunction binding
Unfortunately, the ORCJIT truly lazy function generation callback support is currently disabled. LLVM itself is transitioning to the ORCJIT v2 and in the process
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ namespace Ubiquity.NET.Llvm.Interop
{
if( Handle == IntPtr.Zero )
{
throw new LlvmException( $"[{memberName}] - {sourceFilePath}@{sourceLineNumber} {message} " );
throw new UnexpectedNullHandleException( $"[{memberName}] - {sourceFilePath}@{sourceLineNumber} {message} " );
}

return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// -----------------------------------------------------------------------
// <copyright file="UnexpectedNullInteropHandle.cs" company="Ubiquity.NET Contributors">
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Runtime.Serialization;

namespace Ubiquity.NET.Llvm.Interop
{
/// <summary>Exception thrown when an underlying LLVM handle is unexpectedly null</summary>
/// <remarks>
/// This is generally a non-recoverable error state where the underlying LLVM library is
/// in an inconsistent or unexpected state.
/// </remarks>
public sealed class UnexpectedNullHandleException
: InvalidOperationException
{
/// <summary>Initializes a new instance of the <see cref="UnexpectedNullHandleException"/> class.</summary>
/// <param name="message">Exception error message</param>
public UnexpectedNullHandleException( string message )
: base( message )
{
}

/// <summary>Initializes a new instance of the <see cref="UnexpectedNullHandleException"/> class.</summary>
/// <param name="message">Exception error message</param>
/// <param name="innerException">Inner Exception</param>
public UnexpectedNullHandleException( string message, Exception innerException )
: base( message, innerException )
{
}

/// <summary>Initializes a new instance of the <see cref="UnexpectedNullHandleException"/> class.</summary>
public UnexpectedNullHandleException( )
{
}

/// <summary>Initializes a new instance of the <see cref="UnexpectedNullHandleException"/> class.</summary>
/// <param name="info">Serialization information</param>
/// <param name="context">Context for serialization</param>
public UnexpectedNullHandleException( SerializationInfo info, StreamingContext context )
: base( info, context )
{
}
}
}
Loading

0 comments on commit 4eb1ef2

Please sign in to comment.