anchor.diff:                  match t with
                  
                       | Num -> 
  -                        yield OpConstant(tid, id, value |> getDwords)
  +                        match value with
  +                            | :? float as v when doubleAsFloat ->
  +                                yield OpConstant(tid, id, v |> float32 |> getDwords)
  +                            | _ -> 
  +                                yield OpConstant(tid, id, value |> getDwords)
   
                       | Bool ->
                           if unbox value then yield OpConstantTrue(tid, id)
  
positive.code: 
                      return Some id
  
  
                  | _ ->
                      return None
  
negative.code:                     match t with
                          | None -> return! Expr.Call(pi.GetMethod, indices) |> compileExpression last 
                          | Some t -> return! Expr.Call(t, pi.GetMethod, indices) |> compileExpression last
                              
                  | _ ->
                      return failwithf "unsupported expr: %A" e
  
          }
  
      let compileFunction (e : Expr) =
          spirv {
              match e with
                  | MethodLambda(args, body) ->
                      let! funcType = compileType e.Type
                      let! resType = compileType body.Type
  
                      let! id = newId
                      yield OpFunction(resType, id, FunctionControl.None, funcType)
                      for a in args do
                          let! vid = newMutableVarId a
                          let! at = compileType (makeParameterType a.Type)
                          yield OpFunctionParameter(at, vid)
  
                      let! _ = compileExpression true body
  
                      yield OpFunctionEnd
                      
                  | _ ->
                      ()
  
              
          }
  
  
      let getBuiltInInput (t : ShaderType) (last : Option<ShaderType>) (s : string) =
          match s, last with
              | "PointSize", Some _ -> 
                  Some ("gl_PointSize", BuiltIn.PointSize)
  
              | "ClipDistance", Some _  -> 
                  Some ("gl_ClipDistance", BuiltIn.ClipDistance)
  
              | _ ->
                  match t with
                      | Vertex ->
                          match s with
                              | "VertexId" -> Some ("gl_VertexID", BuiltIn.VertexId)
                              | "InstanceId" -> Some ("gl_InstanceID", BuiltIn.InstanceId)
                              | _ -> None
  
                      | TessControl ->
                          match s with
                              | "PatchVertices" -> Some ("gl_PatchVerticesIn", BuiltIn.PatchVertices)
                              | "PrimitiveId" -> Some ("gl_PrimitiveID", BuiltIn.PrimitiveId)
                              | "InvocationId" -> Some ("gl_InvocationID", BuiltIn.InvocationId)
                              | _ -> None
                      | TessEval ->
                          match s with
                              | "TessCoord" -> Some ("gl_TessCoord", BuiltIn.TessCoord)
                              | "PatchVertices" -> Some ("gl_PatchVerticesIn", BuiltIn.PatchVertices)
                              | "PrimitiveId" -> Some ("gl_PrimitiveID", BuiltIn.PrimitiveId)
                              | "TessLevelInner" -> Some ("gl_TessLevelInner", BuiltIn.TessLevelInner)
                              | "TessLevelOuter" -> Some ("gl_TessLevelOuter", BuiltIn.TessLevelOuter)
                              | _ -> None
  
                      | Geometry(_) ->
                          match s with
                              | "PrimitiveId" -> Some ("gl_PrimitiveIDIn", BuiltIn.PrimitiveId)
                              | "InvocationId" -> Some ("gl_InvocationID", BuiltIn.InvocationId)
                              | _ -> None
  
                      | Fragment ->
                          match s with
                              | "FragCoord" -> Some ("gl_FragCoord", BuiltIn.FragCoord)
                              | "FrontFacing" -> Some ("gl_FrontFacing", BuiltIn.FrontFacing)
                              | "PointCoord" -> Some ("gl_PointCoord", BuiltIn.PointCoord)
                              | "SampleId" -> Some ("gl_SampleID", BuiltIn.SampleId)
                              | "SamplePosition" -> Some ("gl_SamplePosition", BuiltIn.SamplePosition)
                              | "SampleMask" -> Some ("gl_SampleMaskIn", BuiltIn.SampleMask)
                              | "PrimitiveId" -> Some ("gl_PrimitiveID", BuiltIn.PrimitiveId)
                              | "Layer" -> Some ("gl_Layer", BuiltIn.Layer)
                              | "ViewportIndex" -> Some ("gl_ViewportIndex", BuiltIn.ViewportIndex)
                              | _ -> None
  
      let getBuiltInOutput (t : ShaderType) (next : Option<ShaderType>) (s : string) =
          match s, next with
              | "Positions", Some Fragment -> 
                  Some ("gl_Position", BuiltIn.Position)
  
              | "PointSize", Some _ -> 
                  Some ("gl_PointSize", BuiltIn.PointSize)
  
              | "ClipDistance", Some _  -> 
                  Some ("gl_ClipDistance", BuiltIn.ClipDistance)
  
              | _ ->
                  match t with
                      | Vertex ->
                          None
  
                      | TessControl ->
                          match s with
                              | "TessLevelInner" -> Some ("gl_TessLevelInner", BuiltIn.TessLevelInner)
                              | "TessLevelOuter" -> Some ("gl_TessLevelOuter", BuiltIn.TessLevelOuter)
                              | _ -> None
  
                      | TessEval ->
                          None
  
                      | Geometry(_) ->
                          match s with
                              | "Layer" -> Some ("gl_Layer", BuiltIn.Layer)
                              | "ViewportIndex" -> Some ("gl_ViewportIndex", BuiltIn.ViewportIndex)
                              | "PrimitiveId" -> Some ("gl_PrimitiveID", BuiltIn.PrimitiveId)
                              | _ -> None
  
                      | Fragment ->
                          match s with
                              | "Depth" -> Some ("gl_FragDepth", BuiltIn.FragDepth)
                              | _ -> None
  
      let compileShaderFunction (last : Option<ShaderType>) (shaderType : ShaderType) (next : Option<ShaderType>) (uniforms : list<Var>) (inputs : list<Either<int, BuiltIn> * Var>) (outputs : list<Either<int, BuiltIn> * Var>) (body : Expr) =
          spirv {
  
              let model =
                  match shaderType with
                      | ShaderType.Vertex -> ExecutionModel.Vertex
                      | ShaderType.TessControl -> ExecutionModel.TessellationControl
                      | ShaderType.TessEval -> ExecutionModel.TessellationEvaluation
                      | ShaderType.Geometry _ -> ExecutionModel.Geometry
                      | ShaderType.Fragment -> ExecutionModel.Fragment
  
  
              yield OpCapability Capability.Shader
  
              let! glslExt = newId
              yield OpExtInstImport(glslExt, "GLSL.std.450")
              do! setGlslId glslExt
  
              yield OpMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450)
  
              let! entry = newId
              yield OpEntryPoint(model, entry, "main")
  
              match shaderType with
                  | Fragment ->
                      yield OpExecutionMode(entry, ExecutionMode.OriginLowerLeft, None)
  
                  | _ ->
                      // TODO: special annotations per stage
                      ()
  
  
              //yield OpSource(SourceLanguage.Unknown, 100u, "FShade")
              yield OpSourceExtension("GL_ARB_separate_shader_objects")
              yield OpSourceExtension("GL_ARB_shading_language_420pack")
  
              yield OpName(entry, "main")
  
              for (_,i) in inputs do
                  let! id = newMutableVarId i
                  yield OpName(id, i.Name)
  
              for (_,o) in outputs do
                  let! id = newMutableVarId o
                  yield OpName(id, o.Name)
  
  
              for (loc, i) in inputs do
                  let! tid = compileType (makeInputType i.Type)
                  let! vid = getVarId i
                  match vid with
                      | LocationId vid ->
                          yield OpVariable(tid, vid, StorageClass.Input, None)
  
                          match loc with
                              | Left loc -> yield OpDecorate(vid, Decoration.Location, [|uint32 loc|])
                              | Right b -> yield OpDecorate(vid, Decoration.BuiltIn, [|uint32 (int b)|])
  
                      | _ -> 
                          failwith "impossible"
  
              for (loc, o) in outputs do
                  let! tid = compileType (makeOutputType o.Type)
                  let! vid = getVarId o
                  match vid with
                      | LocationId vid ->
                          yield OpVariable(tid, vid, StorageClass.Output, None)
  
                          match loc with
                              | Left loc -> yield OpDecorate(vid, Decoration.Location, [|uint32 loc|])
                              | Right b -> yield OpDecorate(vid, Decoration.BuiltIn, [|uint32 (int b)|])
                      | _ -> 
                          failwith "impossible"
  
              for (u) in uniforms do
                  let! tid = compileType (makeUniformType u.Type)
                  let! vid = newMutableVarId u
                  yield OpVariable(tid, vid, StorageClass.Uniform, None)
                  // TODO: descriptorsets and bindings here
                  //yield OpDecorate(vid, Decoration.Location, [|uint32 loc|])
  
  
  
  
              let! tVoid = compileType typeof<unit>
              let! entryType = compileType typeof<unit -> unit>
              yield OpFunction(tVoid, entry, FunctionControl.None, entryType)
  
              let! _ = compileExpression true body
  
              yield OpFunctionEnd
  
  
  
          }
  
  
      let compileShader (last : Option<ShaderType>) (next : Option<ShaderType>) (shader : Compiled<Shader, ShaderState>) : Compiled<Module, ShaderState> =
          compile {
              let! s = shader
  
  
              // uniform grouping
              let uniformGroups = 
                  s.uniforms  
                      |> Seq.choose(fun (u,v) ->
                          match u with
                              | UserUniform(t,o) -> Some (uniform, t, v.Name,v)
                              | Attribute(scope, t, n) -> Some (scope, t, n,v)
                              | SamplerUniform(t,sem, n,_) -> None
                         ) 
                      |> Seq.groupBy(fun (s,_,_,_) -> s)
                      |> Seq.map (fun (g,v) -> (g, v |> Seq.map (fun (_,t,n,v) -> (t,n,v)) |> Seq.toList))
                      |> Map.ofSeq
  
              let bufferVariables = 
                  uniformGroups |> Map.map (fun scope fields ->
                      let t = CustomStruct(scope.Name, fields |> List.map (fun (t,n,_) -> n,t))   
                      Var(sprintf "_%s" scope.Name, t)
                  )
  
              let variableParents =
                  uniformGroups 
                      |> Map.toList
                      |> List.collect (fun (scope, fields) ->
                          fields |> List.map (fun (_,_,v) ->
                              v, Map.find scope bufferVariables
                          )
                         )
                      |> Map.ofList
  
              let body =
                  s.body.Substitute (fun vi ->
                      match Map.tryFind vi variableParents with
                          | Some buffer ->
                              Expr.FieldGet(Expr.Var buffer, buffer.Type.GetField(vi.Name)) |> Some
                          | None ->
                              None
                  )
  
              let uniforms =
                  List.concat [
                      bufferVariables 
                          |> Map.toList |> List.map snd
  
                      s.uniforms 
                          |> List.choose (fun (u,v) -> 
                              match u with
                                  | SamplerUniform(_) -> Some v
                                  | _ -> None
                              )
                  ]
  
  
  
              // IO builtIns
              let inputs = s.inputs |> Map.toSeq |> Seq.sortBy(fun (_,n) -> n.Name) |> Seq.map snd |> Seq.toList
              let outputs = s.outputs |> Map.toSeq |> Seq.sortBy(fun (_,(_,n)) -> n.Name) |> Seq.map snd |> Seq.toList
  
              let inputRemap = 
                  s.inputs
                      |> Map.toList 
                      |> List.choose (fun (sem,i) ->
                          match getBuiltInInput s.shaderType last sem with
                              | Some (name, builtin) ->
                                  Some (i, (Var(name,i.Type), builtin))
                              | _ ->
                                  None
                         )
                      |> Map.ofList
  
              let outputRemap = 
                  s.outputs
                      |> Map.toList 
                      |> List.choose (fun (sem,(_,o)) ->
                          match getBuiltInOutput s.shaderType next sem with
                              | Some (name, builtin) ->
                                  Some (o, (Var(name,o.Type), builtin))
                              | _ ->
                                  None
                         )
                      |> Map.ofList
  
              let remap = Map.union inputRemap outputRemap
  
              let body =
                  body.Substitute(fun vi ->
                      match Map.tryFind vi remap with
                          | Some (v, builtin) ->
                              Some (Expr.Var v)
                          | None ->
                              None
                  )
  
              let mutable location = 0
              let inputs = 
                  inputs |> List.map (fun i ->
                      match Map.tryFind i inputRemap with
                          | Some (ni, b) -> (Right b, ni)
                          | None -> 
                              let l = location
                              location <- location + 1
                              (Left l, i)
                  )
  
  
              let mutable location = 0
              let outputs = 
                  outputs |> List.map (fun (loc, i) ->
                      match Map.tryFind i outputRemap with
                          | Some (ni, b) -> (Right b, ni)
                          | None -> 
                              let l = location
                              location <- location + 1
                              (Left l, i)
                  )
  
  
  
              // compile to instructions
              let res = compileShaderFunction last s.shaderType next uniforms inputs outputs body
              let (state,_) = res.build SpirVState.Empty
  
  
  
              return { 
                  magic = 0x07230203u
                  version = 0x00010000u
                  generatorMagic = 0xDEADBEEFu
                  bound = state.currentId
                  reserved = 0u
                  instructions = (RevList.toList state.typeInstructions) @ (RevList.toList state.instructions)
              }
          }
  
      let vs (e : Compiled<Effect,_>) =
          compile {
              let! e = e
              return e.vertexShader.Value
          }
  
      let testCode = 
          <@ 
              let ipos = V4d.Zero
              let mutable op = V4d.Zero
  
              op <- ipos
          @>
  
  
      type Vertex =
          {
              [<Semantic("Positions")>] pos : V4d
              [<Semantic("WorldPosition")>] wp : V4d
              [<Semantic("Normals")>] n : V3d
          }
  
      let sh (v : Vertex) =
          vertex {
              let m : M44d = uniform?PerModel?ModelTrafo
              let vp : M44d = uniform?PerView?ViewProjTrafo
              let nm : M33d = uniform?PerModel?NormalMatrix
  
              let wp = m * v.pos
  
              return { v with pos = vp * wp; wp = wp; n = nm * v.n }
          }
  
      let runTest() =
          
          let e = toEffect sh |> vs |> compileShader None (Some Fragment)
  
          match e.runCompile (emptyCompilerState (FakeCompiler() :> ICompiler<_>)) with
              | Success(_,compiled) ->
                  printfn "// Module Version 10000"
                  printfn "// Id's are bound by: %A" compiled.bound
                  let str = InstructionPrinter.toString compiled.instructions
  
                  if System.IO.File.Exists @"C:\Users\Schorsch\Desktop\test.spv" then
                      System.IO.File.Delete @"C:\Users\Schorsch\Desktop\test.spv"
  
                  use s = System.IO.File.OpenWrite(@"C:\Users\Schorsch\Desktop\test.spv")
                  let writer = new System.IO.BinaryWriter(s)
                  SpirV.Serializer.write compiled writer
  
  
  
  
                  printfn "%s" str
  
              | Error e ->
                  failwith e
  
  