Skip to content

Commit 3e880d3

Browse files
authored
Merge pull request #29 from Copilot-Language/develop-import-bdpi
Implement missing/buggy FP operations with `import "BDPI"`. Refs #28.
2 parents 7babe9c + d753520 commit 3e880d3

File tree

7 files changed

+409
-49
lines changed

7 files changed

+409
-49
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2024-11-11
2+
* Implement missing floating-point operations. (#28)
3+
14
2024-11-08
25
* Version bump (4.1). (#25)
36

DESIGN.md

Lines changed: 165 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ are:
6767
2. The call to `compile "Fibs" spec'` in `main`. This will compile the Copilot
6868
specification to a Bluespec program named `Fibs.bs`.
6969

70-
Running this program will generate three files[^1]:
70+
Running this program will generate five files[^1]:
7171

7272
[^1]: The actual code in these files is machine-generated and somewhat
7373
difficult to read. We have cleaned up the code slightly to make it easier to
@@ -127,6 +127,7 @@ understand.
127127
128128
import FibsTypes
129129
import FibsIfc
130+
import BluespecFP
130131
131132
mkFibs :: Module FibsIfc -> Module Empty
132133
mkFibs ifcMod =
@@ -179,6 +180,62 @@ understand.
179180
the structure of a module, whereas the `ActionValue` monad describes the
180181
behavior of a circuit at execution time.
181182

183+
* `BluespecFP.bsv`: A collection of floating-point operations that leverage BDPI
184+
(Bluespec's foreign-function interface). We will omit the full contents of
185+
this file for brevity, but it will look something like this:
186+
187+
```bluespec
188+
import FloatingPoint::*;
189+
190+
import "BDPI" function Float bs_fp_expf (Float x);
191+
import "BDPI" function Double bs_fp_exp (Double x);
192+
import "BDPI" function Float bs_fp_logf (Float x);
193+
import "BDPI" function Double bs_fp_log (Double x);
194+
...
195+
```
196+
197+
For more information on what this file does, see the "Floating-point numbers"
198+
section below.
199+
200+
* `bs_fp.c`: A collection of floating-point operations implemented in C. These
201+
functions are imported via BDPI in `BluespecFP.bsv`. We will omit the full
202+
contents of this file for brevity, but it will look something like this:
203+
204+
```bluespec
205+
#include <math.h>
206+
207+
union ui_float {
208+
unsigned int i;
209+
float f;
210+
};
211+
212+
union ull_double {
213+
unsigned long long i;
214+
double f;
215+
};
216+
217+
unsigned int bs_fp_expf(unsigned int x) {
218+
...
219+
}
220+
221+
unsigned long long bs_fp_exp(unsigned long long x) {
222+
...
223+
}
224+
225+
unsigned int bs_fp_logf(unsigned int x) {
226+
...
227+
}
228+
229+
unsigned long long bs_fp_log(unsigned long long x) {
230+
...
231+
}
232+
233+
...
234+
```
235+
236+
For more information on what this file does, see the "Floating-point numbers"
237+
section below.
238+
182239
In a larger application, a Copilot user would instantiate `mkFibs` with a
183240
`FibsIfc` module that describes what should happen when the `even` and `odd`
184241
triggers fire. `FibsIfc` contains everything that the user must supply;
@@ -222,7 +279,7 @@ code generation for mkTop starts
222279
Elaborated module file created: mkTop.ba
223280
All packages are up to date.
224281
225-
$ bsc -sim -e mkTop -o mkTop.exe
282+
$ bsc -sim -e mkTop -o mkTop.exe bs_fp.c
226283
Bluesim object created: mkTop.{h,o}
227284
Bluesim object created: model_mkTop.{h,o}
228285
Simulation shared library created: mkTop.exe.so
@@ -657,20 +714,25 @@ the common case.
657714

658715
## Floating-point numbers
659716

660-
`copilot-bluespec` has partial support for Copilot's floating-point operations,
661-
as it is limited by what operations are provided by Bluespec's standard
662-
libraries. The following floating-point operations are supported:
717+
`copilot-bluespec` supports all of Copilot's floating-point operations with
718+
varying degrees of performance. The following floating-point operations compile
719+
directly to relatively performant circuits:
663720

664721
* Basic arithmetic (`(+)`, `(-)`, `(*)`, `(/)`)
665722
* Equality checking (`(==)` and `(/=)`)
666723
* Inequality checking (`(<)`, `(<=)`, `(>)`, and `(>=)`)
667724
* `abs`
668725
* `signum`
669726
* `recip`
670-
* `sqrt`
671727

672-
The following floating-point operations are _not_ supported:
728+
These operations correspond to the floating-point operations that the Bluespec
729+
standard library provides that are well tested. Unfortunately, the Bluespec
730+
standard library does not offer well-tested versions (or even _any_ versions)
731+
of the remainder of Copilot's floating-point operations. The rest of these
732+
operations are instead implemented by using BDPI (Bluespec's foreign function
733+
interface) to interface with C code:
673734

735+
* `sqrt`
674736
* `acos`
675737
* `asin`
676738
* `atan`
@@ -691,8 +753,102 @@ The following floating-point operations are _not_ supported:
691753
* `ceiling`
692754
* `floor`
693755

694-
See https://github.com/B-Lang-org/bsc/issues/368 for further discussion on the
695-
unsupported operations.
756+
Implementing these operations via C provides high confidence that they are
757+
implemented correctly, but at a somewhat steep performance penalty.
758+
759+
Because these operations need to be implemented via BDPI, `copilot-bluespec`
760+
generates two additional files: `BluespecFP.bsv` (which contains the Bluespec
761+
function stubs for each function implemented via BDPI) and `bs_fp.c` (which
762+
contains the corresponding C function definitions). To see how this works,
763+
let us take a look at one of the BDPI'd functions, `sqrt`:
764+
765+
```bluespec
766+
import "BDPI" function Double bs_fp_sqrt (Double x);
767+
import "BDPI" function Float bs_fp_sqrtf (Float x);
768+
```
769+
770+
This declares a Bluespec function `bs_fp_sqrt` that is implemented using a C
771+
function (also of the name `bs_fp_sqrt`) under the hood. This takes a Bluespec
772+
`Double` as an argument and also returns a `Double`. Note that `Double` is not
773+
treated magically by the Bluespec compiler here. This is because any Bluespec
774+
struct can be used in BDPI (provided that the struct type implements the `Bits`
775+
class), and Bluespec's `Double` is implemented as a struct with a `Bits`
776+
instance that exactly matches the bit layout expected by IEEE-754
777+
double-precision floats. (Similarly for Bluespec's `Float` type.)
778+
779+
Note that at present, the `import "BDPI"` feature is only available when using
780+
the BSV syntax, not the BH syntax. As such, this is currently the only place
781+
where we generate BSV code.
782+
783+
The corresponding C code for `bs_fp_sqrt(f)` is:
784+
785+
```c
786+
union ull_double {
787+
unsigned long long i;
788+
double f;
789+
};
790+
791+
union ui_float {
792+
unsigned int i;
793+
float f;
794+
};
795+
796+
unsigned long long bs_fp_sqrt(unsigned long long x) {
797+
union ull_double x_u;
798+
union ull_double r_u;
799+
x_u.i = x;
800+
r_u.f = sqrt(x_u.f);
801+
return r_u.i;
802+
}
803+
804+
unsigned int bs_fp_sqrtf(unsigned int x) {
805+
union ui_float x_u;
806+
union ui_float r_u;
807+
x_u.i = x;
808+
r_u.f = sqrtf(x_u.f);
809+
return r_u.i;
810+
}
811+
```
812+
813+
There is a lot to unpack here. Let's go through this step by step:
814+
815+
1. The C version of `bs_fp_sqrt` takes and returns an `unsigned long long`. The
816+
use of `unsigned long long` is dictated by Bluespec itself: whenever you
817+
use a Bluespec type in BDPI that fits in exactly 64 bits, then Bluespec
818+
expects the corresponding C type to be `unsigned long long`. (You can see
819+
this for yourself by inspecting the generated `imported_BDPI_functions.h`
820+
header file.)
821+
822+
There is a similar story for `bs_fp_sqrtf`, which takes an `unsigned int`.
823+
Bluespec dictates the use of `unsigned int` when the BDPI types fits in
824+
exactly 32 bits.
825+
826+
2. This poses something of a challenge for us, since we want the implementation
827+
of `bs_fp_sqrt` to work over `double`s, not `unsigned long long`s. To make
828+
this possible, we define a `union ull_double` type that allows easily
829+
converting an `unsigned long long` to a `double` and vice versa.
830+
831+
There is an analogous story for `ui_float`, which allows conversion to and
832+
from the `unsigned int` and `float` types.
833+
834+
3. Finally, we perform the `sqrt(f)` function on the argument, using
835+
`ull_double`/`ui_float` as necessary to make the types work out.
836+
837+
Strictly speaking, it is only necessary to compile the generated `bs_fp.c` file
838+
if the generated Bluespec program makes use of any of the BDPI-related
839+
floating-point operations mentioned above. That being said, it doesn't hurt to
840+
compile it even if the generated Bluespec program _doesn't_ use any of them, so
841+
it's generally good practice to pass `bs_fp.c` to `bsc`.
842+
843+
Eventually, we would like to stop using BDPI in favor of native Bluespec code,
844+
which would be more performant. To do so, would we need to address the
845+
following Bluespec issues:
846+
847+
* The implementation of `sqrt` in Bluespec's standard library is buggy:
848+
https://github.com/B-Lang-org/bsc/issues/710
849+
850+
* Bluespec's standard library does not implement the remaining floating-point
851+
operations at all: https://github.com/B-Lang-org/bsc/issues/368
696852
697853
## Warnings
698854

copilot-bluespec.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ library
5454
, Copilot.Compile.Bluespec.Error
5555
, Copilot.Compile.Bluespec.Expr
5656
, Copilot.Compile.Bluespec.External
57+
, Copilot.Compile.Bluespec.FloatingPoint
5758
, Copilot.Compile.Bluespec.Name
5859
, Copilot.Compile.Bluespec.Settings
5960
, Copilot.Compile.Bluespec.Type

src/Copilot/Compile/Bluespec/Compile.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import Copilot.Core
2727
-- Internal imports
2828
import Copilot.Compile.Bluespec.CodeGen
2929
import Copilot.Compile.Bluespec.External
30+
import Copilot.Compile.Bluespec.FloatingPoint
3031
import Copilot.Compile.Bluespec.Name
3132
import Copilot.Compile.Bluespec.Settings
3233

@@ -53,6 +54,8 @@ compileWith bsSettings prefix spec
5354
createDirectoryIfMissing True dir
5455
writeFile (dir </> specTypesPkgName prefix ++ ".bs") typesBsFile
5556
writeFile (dir </> specIfcPkgName prefix ++ ".bs") ifcBsFile
57+
writeFile (dir </> "bs_fp.c") copilotBluespecFloatingPointC
58+
writeFile (dir </> "BluespecFP.bsv") copilotBluespecFloatingPointBSV
5659
writeFile (dir </> prefix ++ ".bs") bsFile
5760

5861
-- | Compile a specification to a Bluespec.
@@ -106,6 +109,7 @@ compileBS _bsSettings prefix spec =
106109
$ specTypesPkgName prefix
107110
, BS.CImpId False $ BS.mkId BS.NoPos $ fromString
108111
$ specIfcPkgName prefix
112+
, BS.CImpId False $ BS.mkId BS.NoPos "BluespecFP"
109113
]
110114

111115
moduleDef :: BS.CDefn

src/Copilot/Compile/Bluespec/Expr.hs

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE BangPatterns #-}
22
{-# LANGUAGE GADTs #-}
33
{-# LANGUAGE OverloadedStrings #-}
4+
{-# LANGUAGE ScopedTypeVariables #-}
45

56
-- | Translate Copilot Core expressions and operators to Bluespec.
67
module Copilot.Compile.Bluespec.Expr
@@ -81,38 +82,37 @@ transOp1 op e =
8182
(BS.CVar (BS.idSlashAt BS.NoPos))
8283
[constNumTy ty 1, e]
8384
BwNot _ty -> app $ BS.idInvertAt BS.NoPos
84-
Sqrt _ty -> BS.CSelect
85-
(BS.CApply
86-
(BS.CVar (BS.mkId BS.NoPos "sqrtFP"))
87-
[e, fpRM])
88-
BS.idPrimFst
8985

9086
Cast fromTy toTy -> transCast fromTy toTy e
9187
GetField (Struct _) _ f -> BS.CSelect e $ BS.mkId BS.NoPos $
9288
fromString $ lowercaseName $ accessorName f
9389
GetField _ _ _ -> impossible "transOp1" "copilot-bluespec"
9490

95-
-- Unsupported operations (see
96-
-- https://github.com/B-Lang-org/bsc/discussions/534)
97-
Exp _ty -> unsupportedFPOp "exp"
98-
Log _ty -> unsupportedFPOp "log"
99-
Acos _ty -> unsupportedFPOp "acos"
100-
Asin _ty -> unsupportedFPOp "asin"
101-
Atan _ty -> unsupportedFPOp "atan"
102-
Cos _ty -> unsupportedFPOp "cos"
103-
Sin _ty -> unsupportedFPOp "sin"
104-
Tan _ty -> unsupportedFPOp "tan"
105-
Acosh _ty -> unsupportedFPOp "acosh"
106-
Asinh _ty -> unsupportedFPOp "asinh"
107-
Atanh _ty -> unsupportedFPOp "atanh"
108-
Cosh _ty -> unsupportedFPOp "cosh"
109-
Sinh _ty -> unsupportedFPOp "sinh"
110-
Tanh _ty -> unsupportedFPOp "tanh"
111-
Ceiling _ty -> unsupportedFPOp "ceiling"
112-
Floor _ty -> unsupportedFPOp "floor"
91+
-- BDPI-supported operations
92+
Sqrt ty -> appFP ty "sqrt"
93+
Exp ty -> appFP ty "exp"
94+
Log ty -> appFP ty "log"
95+
Acos ty -> appFP ty "acos"
96+
Asin ty -> appFP ty "asin"
97+
Atan ty -> appFP ty "atan"
98+
Cos ty -> appFP ty "cos"
99+
Sin ty -> appFP ty "sin"
100+
Tan ty -> appFP ty "tan"
101+
Acosh ty -> appFP ty "acosh"
102+
Asinh ty -> appFP ty "asinh"
103+
Atanh ty -> appFP ty "atanh"
104+
Cosh ty -> appFP ty "cosh"
105+
Sinh ty -> appFP ty "sinh"
106+
Tanh ty -> appFP ty "tanh"
107+
Ceiling ty -> appFP ty "ceiling"
108+
Floor ty -> appFP ty "floor"
113109
where
110+
app :: BS.Id -> BS.CExpr
114111
app i = BS.CApply (BS.CVar i) [e]
115112

113+
appFP :: forall t. Type t -> String -> BS.CExpr
114+
appFP ty funPrefix = app $ fpFunId ty funPrefix
115+
116116
-- | Translates a Copilot binary operator and its arguments into a Bluespec
117117
-- function.
118118
transOp2 :: Op2 a b c -> BS.CExpr -> BS.CExpr -> BS.CExpr
@@ -144,14 +144,17 @@ transOp2 op e1 e2 =
144144
BS.CStructUpd e1 [(BS.mkId BS.NoPos field, e2)]
145145
UpdateField _ _ _ -> impossible "transOp2" "copilot-bluespec"
146146

147-
-- Unsupported operations (see
148-
-- https://github.com/B-Lang-org/bsc/discussions/534)
149-
Pow _ty -> unsupportedFPOp "(**)"
150-
Logb _ty -> unsupportedFPOp "logb"
151-
Atan2 _ty -> unsupportedFPOp "atan2"
147+
-- BDPI-supported operations
148+
Pow ty -> appFP ty "pow"
149+
Logb ty -> appFP ty "logb"
150+
Atan2 ty -> appFP ty "atan2"
152151
where
152+
app :: BS.Id -> BS.CExpr
153153
app i = BS.CApply (BS.CVar i) [e1, e2]
154154

155+
appFP :: forall t. Type t -> String -> BS.CExpr
156+
appFP ty funPrefix = app $ fpFunId ty funPrefix
157+
155158
-- | Translates a Copilot ternary operator and its arguments into a Bluespec
156159
-- function.
157160
transOp3 :: Op3 a b c d -> BS.CExpr -> BS.CExpr -> BS.CExpr -> BS.CExpr
@@ -637,6 +640,19 @@ cUpdateVector vec idx newElem =
637640
(BS.CVar (BS.mkId BS.NoPos "update"))
638641
[vec, idx, newElem]
639642

643+
-- | Create a Bluespec identifier for a floating-point function that Bluespec
644+
-- imports using BDPI.
645+
fpFunId :: Type a -> String -> BS.Id
646+
fpFunId ty funPrefix =
647+
BS.mkId BS.NoPos $ fromString $ "bs_fp_" ++ funName
648+
where
649+
funName :: String
650+
funName =
651+
case ty of
652+
Float -> funPrefix ++ "f"
653+
Double -> funPrefix
654+
_ -> impossible "fpFunId" "copilot-bluespec"
655+
640656
-- | Explicitly annotate an expression with a type signature. This is necessary
641657
-- to prevent expressions from having ambiguous types in certain situations.
642658
withTypeAnnotation :: Type a -> BS.CExpr -> BS.CExpr
@@ -648,13 +664,6 @@ typeIsFloating Float = True
648664
typeIsFloating Double = True
649665
typeIsFloating _ = False
650666

651-
-- | Throw an error if attempting to use a floating-point operation that
652-
-- Bluespec does not currently support.
653-
unsupportedFPOp :: String -> a
654-
unsupportedFPOp op =
655-
error $ "Bluespec's FloatingPoint type does not support the " ++ op ++
656-
" operation."
657-
658667
-- | We assume round-near-even throughout, but this variable can be changed if
659668
-- needed. This matches the behavior of @fpRM@ in @copilot-theorem@'s
660669
-- @Copilot.Theorem.What4.Translate@ module.

0 commit comments

Comments
 (0)