microsoft / qsharp-compiler

Q# compiler, command line tool, and Q# language server
https://docs.microsoft.com/quantum
MIT License
683 stars 172 forks source link

Missing validation for attaching TargetInstruction attribute #890

Open msoeken opened 3 years ago

msoeken commented 3 years ago

Describe the bug

The QIR output does not distinguish between a body and adjoint call.

To Reproduce

Call botnet build on the following project:

<Project Sdk="Microsoft.Quantum.Sdk/0.15.2102129941-alpha">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp5.0</TargetFramework>
    <PlatformTarget>x64</PlatformTarget>
    <QirGeneration>true</QirGeneration>
    <CSharpGeneration>false</CSharpGeneration>
    <IncludeQSharpCorePackages>false</IncludeQSharpCorePackages>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Quantum.QSharp.Foundation" Version="0.15.2102129941-alpha" />
  </ItemGroup>

</Project>
// Program.cs
return 0
// Program.qs
namespace Example {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Targeting;

    @TargetInstruction("instruction")
    operation Instruction() : Unit is Adj {
        body intrinsic;
        adjoint intrinsic;
    }

    @EntryPoint()
    operation RunProgram() : Unit {
        within { Instruction(); }
        apply {
            Message("Hello");
        }
    }
}

Expected behavior

I expect two LLVM declarations for @__quantum__qis__instruction__body and @__quantum__qis__instruction__adj.

Actual behavior

Body and adjoint variants map to the same LLVM declaration @__quantum__qis__instruction:

%Result = type opaque
%Range = type { i64, i64, i64 }
%String = type opaque

@ResultZero = external global %Result*
@ResultOne = external global %Result*
@PauliI = constant i2 0
@PauliX = constant i2 1
@PauliY = constant i2 -1
@PauliZ = constant i2 -2
@EmptyRange = internal constant %Range { i64 0, i64 1, i64 -1 }
@0 = internal constant [6 x i8] c"Hello\00"

@Example__RunProgram = alias void (), void ()* @Example__RunProgram__body

define void @Example__RunProgram__body() #0 {
entry:
  call void @__quantum__qis__instruction()
  %msg = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0))
  call void @__quantum__qis__message__body(%String* %msg)
  call void @__quantum__rt__string_update_reference_count(%String* %msg, i64 -1)
  call void @__quantum__qis__instruction()
  ret void
}

declare void @__quantum__qis__instruction()

declare %String* @__quantum__rt__string_create(i32, i8*)

declare void @__quantum__qis__message__body(%String*)

declare void @__quantum__rt__string_update_reference_count(%String*, i64)

define void @Example__Instruction__body() {
entry:
  call void @__quantum__qis__instruction()
  ret void
}

define void @Example__Instruction__adj() {
entry:
  call void @__quantum__qis__instruction()
  ret void
}

define void @Microsoft__Quantum__Intrinsic__Message__body(%String* %msg) {
entry:
  call void @__quantum__qis__message__body(%String* %msg)
  ret void
}

attributes #0 = { "EntryPoint" }

System information

bettinaheim commented 3 years ago

@msoeken This is a case of the necessary validation not yet having been implemented, sorry. The idea is that you shouldn't be able to attach a TargetInstruction attribute to an operation that has more than one specialization; you would need to manually write what the compiler would otherwise do; the compiler translates

operation Foo () : Unit is Adj {
   body intrinsic;
}

into

@Inline()
operation Foo () : Unit is Adj {
    body (...) {
       Foo__body();
    }
    adjoint (...) {
       Foo__adj();
    } 
}
@TargetInstruction("Foo__body")
operation Foo__body() : Unit {
    body intrinsic;
}
@TargetInstruction("Foo__adj")
operation Foo__adj() : Unit {
    body intrinsic;
}
msoeken commented 3 years ago

Thanks for the clarification