Skip to content

Commit

Permalink
Bitwise operations (#6090)
Browse files Browse the repository at this point in the history
* Initial bitwise primitives

* Wire up new builtins

* Tests

* Changelogs

* Fix failing goldens

* Fix cost model for tests

* Bitwise primitives are not in Conway

* Finish shift tests

* Fix goldens

* Rest of tests

* Rename operations

* Note about split composition for shift property

* Explain bitwise tests in comments, remove AND and OR tests for finding and counting bits

* Goldens for bitwise primops

* Chop down property test running times a bit

* Add test for finding first in zero byte string, rename some tests for clarity

* Clarify implementation choices in the comments

* Tidy up helpers for property tests

* Consolidate all bitwise ops, retarget links to CIPs
  • Loading branch information
kozross committed Jun 13, 2024
1 parent 3b20c63 commit 2ec9bf3
Show file tree
Hide file tree
Showing 25 changed files with 2,135 additions and 1,238 deletions.
39 changes: 39 additions & 0 deletions plutus-core/changelog.d/20240523_124004_koz.ross_bitwise_2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!--
A new scriv changelog fragment.
Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Removed
- A bullet item for the Removed category.
-->
### Added

- Implementation and tests for primitive operations in [this
CIP](https://github.com/mlabs-haskell/CIPs/blob/koz/bitwise/CIP-XXXX/CIP-XXXX.md)

### Changed

- Rename `ReplicateByteString` to `ReplicateByte` (and similarly for denotation)

<!--
### Deprecated
- A bullet item for the Deprecated category.
-->
<!--
### Fixed
- A bullet item for the Fixed category.
-->
<!--
### Security
- A bullet item for the Security category.
-->
5 changes: 3 additions & 2 deletions plutus-core/plutus-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ library
PlutusCore.Analysis.Definitions
PlutusCore.Annotation
PlutusCore.Arity
PlutusCore.Bitwise.Convert
PlutusCore.Bitwise.Logical
PlutusCore.Bitwise
PlutusCore.Builtin
PlutusCore.Builtin.Debug
PlutusCore.Builtin.Elaborate
Expand Down Expand Up @@ -417,6 +416,7 @@ test-suite untyped-plutus-core-test
DeBruijn.Spec
DeBruijn.UnDeBruijnify
Evaluation.Builtins
Evaluation.Builtins.Bitwise
Evaluation.Builtins.BLS12_381
Evaluation.Builtins.BLS12_381.TestClasses
Evaluation.Builtins.BLS12_381.Utils
Expand All @@ -430,6 +430,7 @@ test-suite untyped-plutus-core-test
Evaluation.Debug
Evaluation.FreeVars
Evaluation.Golden
Evaluation.Helpers
Evaluation.Machines
Evaluation.Regressions
Flat.Spec
Expand Down
1,168 changes: 1,168 additions & 0 deletions plutus-core/plutus-core/src/PlutusCore/Bitwise.hs

Large diffs are not rendered by default.

544 changes: 0 additions & 544 deletions plutus-core/plutus-core/src/PlutusCore/Bitwise/Convert.hs

This file was deleted.

464 changes: 0 additions & 464 deletions plutus-core/plutus-core/src/PlutusCore/Bitwise/Logical.hs

This file was deleted.

84 changes: 65 additions & 19 deletions plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
Expand All @@ -26,8 +25,7 @@ import PlutusCore.Evaluation.Machine.ExMemoryUsage (ExMemoryUsage, LiteralByteSi
import PlutusCore.Evaluation.Result (EvaluationResult (..))
import PlutusCore.Pretty (PrettyConfigPlc)

import PlutusCore.Bitwise.Convert as Convert
import PlutusCore.Bitwise.Logical as Logical
import PlutusCore.Bitwise qualified as Bitwise
import PlutusCore.Crypto.BLS12_381.G1 qualified as BLS12_381.G1
import PlutusCore.Crypto.BLS12_381.G2 qualified as BLS12_381.G2
import PlutusCore.Crypto.BLS12_381.Pairing qualified as BLS12_381.Pairing
Expand Down Expand Up @@ -160,7 +158,12 @@ data DefaultFun
| ComplementByteString
| ReadBit
| WriteBits
| ReplicateByteString
| ReplicateByte
-- Bitwise
| ShiftByteString
| RotateByteString
| CountSetBits
| FindFirstSetBit
deriving stock (Show, Eq, Ord, Enum, Bounded, Generic, Ix)
deriving anyclass (NFData, Hashable, PrettyBy PrettyConfigPlc)

Expand Down Expand Up @@ -1817,15 +1820,15 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where
let integerToByteStringDenotation :: Bool -> LiteralByteSize -> Integer -> BuiltinResult BS.ByteString
{- The second argument is wrapped in a LiteralByteSize to allow us to interpret it as a size during
costing. It appears as an integer in UPLC: see Note [Integral types as Integer]. -}
integerToByteStringDenotation b (LiteralByteSize w) n = integerToByteStringWrapper b w n
integerToByteStringDenotation b (LiteralByteSize w) = Bitwise.integerToByteStringWrapper b w
{-# INLINE integerToByteStringDenotation #-}
in makeBuiltinMeaning
integerToByteStringDenotation
(runCostingFunThreeArguments . paramIntegerToByteString)

toBuiltinMeaning _semvar ByteStringToInteger =
let byteStringToIntegerDenotation :: Bool -> BS.ByteString -> Integer
byteStringToIntegerDenotation = byteStringToIntegerWrapper
byteStringToIntegerDenotation = Bitwise.byteStringToIntegerWrapper
{-# INLINE byteStringToIntegerDenotation #-}
in makeBuiltinMeaning
byteStringToIntegerDenotation
Expand All @@ -1834,60 +1837,94 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where
-- Logical
toBuiltinMeaning _semvar AndByteString =
let andByteStringDenotation :: Bool -> BS.ByteString -> BS.ByteString -> BS.ByteString
andByteStringDenotation = Logical.andByteString
andByteStringDenotation = Bitwise.andByteString
{-# INLINE andByteStringDenotation #-}
in makeBuiltinMeaning
andByteStringDenotation
(runCostingFunThreeArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar OrByteString =
let orByteStringDenotation :: Bool -> BS.ByteString -> BS.ByteString -> BS.ByteString
orByteStringDenotation = Logical.orByteString
orByteStringDenotation = Bitwise.orByteString
{-# INLINE orByteStringDenotation #-}
in makeBuiltinMeaning
orByteStringDenotation
(runCostingFunThreeArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar XorByteString =
let xorByteStringDenotation :: Bool -> BS.ByteString -> BS.ByteString -> BS.ByteString
xorByteStringDenotation = Logical.xorByteString
xorByteStringDenotation = Bitwise.xorByteString
{-# INLINE xorByteStringDenotation #-}
in makeBuiltinMeaning
xorByteStringDenotation
(runCostingFunThreeArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar ComplementByteString =
let complementByteStringDenotation :: BS.ByteString -> BS.ByteString
complementByteStringDenotation = Logical.complementByteString
complementByteStringDenotation = Bitwise.complementByteString
{-# INLINE complementByteStringDenotation #-}
in makeBuiltinMeaning
complementByteStringDenotation
(runCostingFunOneArgument . unimplementedCostingFun)

toBuiltinMeaning _semvar ReadBit =
let readBitDenotation :: BS.ByteString -> Int -> BuiltinResult Bool
readBitDenotation = Logical.readBit
readBitDenotation = Bitwise.readBit
{-# INLINE readBitDenotation #-}
in makeBuiltinMeaning
readBitDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar WriteBits =
let writeBitsDenotation :: BS.ByteString -> [(Integer, Bool)] -> BuiltinResult BS.ByteString
writeBitsDenotation = Logical.writeBits
writeBitsDenotation = Bitwise.writeBits
{-# INLINE writeBitsDenotation #-}
in makeBuiltinMeaning
writeBitsDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar ReplicateByteString =
let byteStringReplicateDenotation :: Int -> Word8 -> BuiltinResult BS.ByteString
byteStringReplicateDenotation = Logical.replicateByteString
{-# INLINE byteStringReplicateDenotation #-}
toBuiltinMeaning _semvar ReplicateByte =
let replicateByteDenotation :: Int -> Word8 -> BuiltinResult BS.ByteString
replicateByteDenotation = Bitwise.replicateByte
{-# INLINE replicateByteDenotation #-}
in makeBuiltinMeaning
byteStringReplicateDenotation
replicateByteDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

-- Bitwise

toBuiltinMeaning _semvar ShiftByteString =
let shiftByteStringDenotation :: BS.ByteString -> Int -> BS.ByteString
shiftByteStringDenotation = Bitwise.shiftByteString
{-# INLINE shiftByteStringDenotation #-}
in makeBuiltinMeaning
shiftByteStringDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar RotateByteString =
let rotateByteStringDenotation :: BS.ByteString -> Int -> BS.ByteString
rotateByteStringDenotation = Bitwise.rotateByteString
{-# INLINE rotateByteStringDenotation #-}
in makeBuiltinMeaning
rotateByteStringDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar CountSetBits =
let countSetBitsDenotation :: BS.ByteString -> Int
countSetBitsDenotation = Bitwise.countSetBits
{-# INLINE countSetBitsDenotation #-}
in makeBuiltinMeaning
countSetBitsDenotation
(runCostingFunOneArgument . unimplementedCostingFun)

toBuiltinMeaning _semvar FindFirstSetBit =
let findFirstSetBitDenotation :: BS.ByteString -> Int
findFirstSetBitDenotation = Bitwise.findFirstSetBit
{-# INLINE findFirstSetBitDenotation #-}
in makeBuiltinMeaning
findFirstSetBitDenotation
(runCostingFunOneArgument . unimplementedCostingFun)

-- See Note [Inlining meanings of builtins].
{-# INLINE toBuiltinMeaning #-}

Expand Down Expand Up @@ -2021,7 +2058,12 @@ instance Flat DefaultFun where
ComplementByteString -> 78
ReadBit -> 79
WriteBits -> 80
ReplicateByteString -> 81
ReplicateByte -> 81

ShiftByteString -> 82
RotateByteString -> 83
CountSetBits -> 84
FindFirstSetBit -> 85

decode = go =<< decodeBuiltin
where go 0 = pure AddInteger
Expand Down Expand Up @@ -2105,7 +2147,11 @@ instance Flat DefaultFun where
go 78 = pure ComplementByteString
go 79 = pure ReadBit
go 80 = pure WriteBits
go 81 = pure ReplicateByteString
go 81 = pure ReplicateByte
go 82 = pure ShiftByteString
go 83 = pure RotateByteString
go 84 = pure CountSetBits
go 85 = pure FindFirstSetBit
go t = fail $ "Failed to decode builtin tag, got: " ++ show t

size _ n = n + builtinTagWidth
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bytestring -> integer
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bytestring -> integer
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
integer -> integer -> bytestring
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bytestring -> integer -> bytestring
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bytestring -> integer -> bytestring
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,15 @@ isCommutative = \case
IntegerToByteString -> False
ByteStringToInteger -> False
-- Currently, this requires commutativity in all arguments, which the
-- logical operations are not.
-- logical and bitwise operations are not.
AndByteString -> False
OrByteString -> False
XorByteString -> False
ComplementByteString -> False
ReadBit -> False
WriteBits -> False
ReplicateByteString -> False
ReplicateByte -> False
ShiftByteString -> False
RotateByteString -> False
CountSetBits -> False
FindFirstSetBit -> False
Loading

1 comment on commit 2ec9bf3

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Plutus Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: 2ec9bf3 Previous: 3b20c63 Ratio
validation-decode-auction_1-1 197.3 μs 186.3 μs 1.06
validation-decode-auction_1-2 559.8 μs 522 μs 1.07
validation-decode-auction_1-4 197.3 μs 186.8 μs 1.06
validation-decode-auction_2-1 197.4 μs 186.3 μs 1.06
validation-decode-auction_2-2 557.2 μs 528.7 μs 1.05
validation-decode-auction_2-3 559.8 μs 532.7 μs 1.05
validation-decode-auction_2-5 197.8 μs 186.8 μs 1.06
validation-decode-crowdfunding-success-1 241.4 μs 228.4 μs 1.06
validation-decode-crowdfunding-success-2 241.3 μs 228.8 μs 1.05
validation-decode-crowdfunding-success-3 241.4 μs 228.4 μs 1.06
validation-decode-escrow-redeem_1-1 324.2 μs 307.2 μs 1.06
validation-decode-escrow-redeem_1-2 324.2 μs 307.3 μs 1.05
validation-decode-escrow-redeem_2-2 324.5 μs 309 μs 1.05
validation-decode-future-pay-out-1 241.6 μs 227.9 μs 1.06
validation-decode-uniswap-5 756.3 μs 709.5 μs 1.07

This comment was automatically generated by workflow using github-action-benchmark.

CC: @IntersectMBO/plutus-core

Please sign in to comment.