Skip to content

Commit

Permalink
Use OPAQUE pragma on stringToBuiltinByteString and stringToBuiltinStr…
Browse files Browse the repository at this point in the history
…ing (#6501)
  • Loading branch information
zliu41 authored Sep 19, 2024
1 parent 7178ee7 commit e94d73c
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 27 deletions.
8 changes: 4 additions & 4 deletions plutus-tx-plugin/src/PlutusTx/Compiler/Expr.hs
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ This is very fiddly:
- Sometimes the selector has been inlined.
- We can't easily get access to the name of the method definition itself, so instead we mark
that as INLINE and look for a special function ('stringToBuiltinString') that is in its
body (which we put inside 'noinline', see Note [noinline hack]).
body (and we use the OPAQUE pragma on that function to ensure it isn't inlined).
- Sometimes our heuristics fail.
- The actual definition of 'stringToBuiltinString' works, so in the worst case we fall back
to using it and converting the list of characters into an expression.
Expand Down Expand Up @@ -751,11 +751,11 @@ compileExpr e = traceCompilation 2 ("Compiling expr:" GHC.<+> GHC.ppr e) $ do
Nothing ->
throwSd UnsupportedError $
"Use of fromString on type other than builtin strings or bytestrings:" GHC.<+> GHC.ppr ty
-- 'stringToBuiltinByteString' invocation, will be wrapped in a 'noinline'
-- 'stringToBuiltinByteString' invocation
(strip -> GHC.Var n) `GHC.App` (strip -> stringExprContent -> Just bs)
| GHC.getName n == sbbsName ->
pure $ PIR.Constant annMayInline $ PLC.someValue bs
-- 'stringToBuiltinString' invocation, will be wrapped in a 'noinline'
-- 'stringToBuiltinString' invocation
(strip -> GHC.Var n) `GHC.App` (strip -> stringExprContent -> Just bs) | GHC.getName n == sbsName ->
case TE.decodeUtf8' bs of
Right t -> pure $ PIR.Constant annMayInline $ PLC.someValue t
Expand Down Expand Up @@ -785,7 +785,7 @@ compileExpr e = traceCompilation 2 ("Compiling expr:" GHC.<+> GHC.ppr e) $ do
-- Unboxed unit, (##).
GHC.Var (GHC.idDetails -> GHC.DataConWorkId dc) | dc == GHC.unboxedUnitDataCon -> pure (PIR.mkConstant annMayInline ())
-- Ignore the magic 'noinline' function, it's the identity but has no unfolding.
-- See Note [noinline hack]
-- See Note [GHC.Magic.noinline]
GHC.Var n `GHC.App` GHC.Type _ `GHC.App` arg | GHC.getName n == GHC.noinlineIdName -> compileExpr arg
-- See Note [GHC runtime errors]
-- <error func> <runtime rep> <overall type> <call stack> <message>
Expand Down
1 change: 0 additions & 1 deletion plutus-tx/plutus-tx.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ library
, deriving-compat
, extra
, flat ^>=0.6
, ghc-prim
, hashable
, lens
, memory
Expand Down
28 changes: 6 additions & 22 deletions plutus-tx/src/PlutusTx/Builtins/HasOpaque.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
Expand All @@ -21,14 +20,13 @@ import PlutusTx.Builtins.Internal
import Data.Kind qualified as GHC
import Data.String (IsString (..))
import Data.Text qualified as Text
import GHC.Magic qualified as Magic
import Prelude qualified as Haskell (String)
#if MIN_VERSION_base(4,20,0)
import Prelude (type (~))
#endif


{- Note [noinline hack]
{- Note [GHC.Magic.noinline]
For some functions we have two conflicting desires:
- We want to have the unfolding available for the plugin.
- We don't want the function to *actually* get inlined before the plugin runs, since we rely
Expand All @@ -42,42 +40,28 @@ that function is compiled later into the body of another function.
We do therefore need to handle 'noinline' in the plugin, as it itself does not have
an unfolding.
Another annoying quirk: even if you have 'noinline'd a function call, if the body is
a single variable, it will still inline! This is the case for the obvious definition
of 'stringToBuiltinString' (since the newtype constructor vanishes), so we have to add
some obfuscation to the body to prevent it inlining.
-}

obfuscatedId :: a -> a
obfuscatedId a = a
{-# NOINLINE obfuscatedId #-}

stringToBuiltinByteString :: Haskell.String -> BuiltinByteString
stringToBuiltinByteString str = encodeUtf8 $ stringToBuiltinString str
{-# INLINABLE stringToBuiltinByteString #-}
{-# OPAQUE stringToBuiltinByteString #-}

stringToBuiltinString :: Haskell.String -> BuiltinString
-- To explain why the obfuscatedId is here
-- See Note [noinline hack]
stringToBuiltinString str = obfuscatedId (BuiltinString $ Text.pack str)
{-# INLINABLE stringToBuiltinString #-}
stringToBuiltinString str = BuiltinString (Text.pack str)
{-# OPAQUE stringToBuiltinString #-}

{- Same noinline hack as with `String` type. -}
instance IsString BuiltinByteString where
-- Try and make sure the dictionary selector goes away, it's simpler to match on
-- the application of 'stringToBuiltinByteString'
-- See Note [noinline hack]
fromString = Magic.noinline stringToBuiltinByteString
fromString = stringToBuiltinByteString
{-# INLINE fromString #-}

-- We can't put this in `Builtins.hs`, since that force `O0` deliberately, which prevents
-- the unfoldings from going in. So we just stick it here. Fiddly.
instance IsString BuiltinString where
-- Try and make sure the dictionary selector goes away, it's simpler to match on
-- the application of 'stringToBuiltinString'
-- See Note [noinline hack]
fromString = Magic.noinline stringToBuiltinString
fromString = stringToBuiltinString
{-# INLINE fromString #-}

{- Note [Built-in types and their Haskell counterparts]
Expand Down

1 comment on commit e94d73c

@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: e94d73c Previous: 7178ee7 Ratio
validation-crowdfunding-success-1 293.5 μs 215.5 μs 1.36
validation-crowdfunding-success-2 286.1 μs 243.5 μs 1.17
validation-multisig-sm-2 542.8 μs 382.5 μs 1.42
validation-multisig-sm-3 548.9 μs 385.8 μs 1.42
validation-multisig-sm-4 551.6 μs 387.7 μs 1.42
validation-multisig-sm-5 782.8 μs 551.5 μs 1.42
validation-multisig-sm-6 554.3 μs 391.9 μs 1.41
validation-multisig-sm-7 543.9 μs 383.1 μs 1.42
validation-multisig-sm-8 549.1 μs 385 μs 1.43
validation-multisig-sm-9 554.7 μs 391 μs 1.42
validation-multisig-sm-10 783.9 μs 549.9 μs 1.43
validation-ping-pong-1 455.2 μs 321 μs 1.42
validation-ping-pong-2 455.4 μs 320.2 μs 1.42
validation-stablecoin_1-5 1873 μs 1317 μs 1.42
validation-stablecoin_1-6 354.2 μs 250 μs 1.42
validation-stablecoin_2-1 1268 μs 894.6 μs 1.42
validation-stablecoin_2-2 272 μs 190.9 μs 1.42
validation-stablecoin_2-3 1452 μs 1025 μs 1.42
validation-stablecoin_2-4 286.5 μs 201.5 μs 1.42
validation-token-account-1 264.1 μs 186.2 μs 1.42
validation-token-account-2 471 μs 331.5 μs 1.42
validation-uniswap-2 308.2 μs 242.3 μs 1.27
validation-uniswap-5 1217 μs 1137 μs 1.07
validation-uniswap-6 447.6 μs 313.9 μs 1.43
validation-vesting-1 486.8 μs 342 μs 1.42
validation-decode-crowdfunding-success-3 335.6 μs 274.8 μs 1.22
validation-decode-currency-1 330.2 μs 236.4 μs 1.40
validation-decode-escrow-redeem_1-1 451.1 μs 416.4 μs 1.08
validation-decode-future-pay-out-3 457.3 μs 391.2 μs 1.17
validation-decode-future-pay-out-4 963.4 μs 667.3 μs 1.44
validation-decode-future-settle-early-1 330.2 μs 227.9 μs 1.45
validation-decode-future-settle-early-2 451.1 μs 313 μs 1.44
validation-decode-future-settle-early-3 456.9 μs 313.1 μs 1.46
validation-decode-future-settle-early-4 965.3 μs 666.7 μs 1.45
validation-decode-game-sm-success_1-1 734.6 μs 508.9 μs 1.44
validation-decode-game-sm-success_1-2 231.6 μs 160.5 μs 1.44
validation-decode-game-sm-success_1-3 736.3 μs 511.9 μs 1.44
validation-decode-game-sm-success_1-4 233.9 μs 160.2 μs 1.46
validation-decode-game-sm-success_2-1 732 μs 511 μs 1.43
validation-decode-game-sm-success_2-2 232 μs 161.4 μs 1.44
validation-decode-game-sm-success_2-3 736.3 μs 511.2 μs 1.44
validation-decode-game-sm-success_2-4 182.2 μs 161.1 μs 1.13
validation-decode-game-sm-success_2-6 181.4 μs 161.5 μs 1.12
validation-decode-ping-pong-1 618.7 μs 477.9 μs 1.29
validation-decode-ping-pong-2 539.7 μs 484.5 μs 1.11
validation-decode-ping-pong_2-1 686.2 μs 477.4 μs 1.44
validation-decode-prism-1 228.3 μs 158.5 μs 1.44
validation-decode-prism-2 720 μs 559.7 μs 1.29
validation-decode-stablecoin_2-3 1204 μs 1089 μs 1.11
validation-decode-stablecoin_2-4 234 μs 160.4 μs 1.46
validation-decode-token-account-1 314.7 μs 253.4 μs 1.24
nofib-clausify/formula4 36740 μs 25900 μs 1.42
nofib-clausify/formula5 70410 μs 54730 μs 1.29
nofib-primetest/30digits 65120.00000000001 μs 61880 μs 1.05
nofib-primetest/50digits 142700 μs 102200 μs 1.40
nofib-queens4x4/bt 7603 μs 5373 μs 1.42
nofib-queens4x4/bm 9670 μs 6845 μs 1.41
nofib-queens4x4/bjbt1 7098 μs 6555 μs 1.08
nofib-queens4x4/bjbt2 8675 μs 6628 μs 1.31
marlowe-semantics/1a573aed5c46d637919ccb5548dfc22a55c9fc38298d567d15ee9f2eea69d89e 1299 μs 1207 μs 1.08
marlowe-semantics/1d56060c3b271226064c672a282663643b1b0823471c67737f0b076870331260 1124 μs 779 μs 1.44
marlowe-semantics/1d6e3c137149a440f35e0efc685b16bfb8052ebcf66ec4ad77e51c11501381c7 437 μs 356.9 μs 1.22
marlowe-semantics/1f0f02191604101e1f201016171604060d010d1d1c150e110a110e1006160a0d 1438 μs 957.5 μs 1.50
marlowe-semantics/2797d7ac77c1b6aff8e42cf9a47fa86b1e60f22719a996871ad412cbe4de78b5 2649 μs 2456 μs 1.08
marlowe-semantics/3bb75b2e53eb13f718eacd3263ab4535f9137fabffc9de499a0de7cabb335479 431.1 μs 301.7 μs 1.43
marlowe-semantics/3db496e6cd39a8b888a89d0de07dace4397878958cab3b9d9353978b08c36d8a 1204 μs 834.8 μs 1.44
marlowe-semantics/44a9e339fa25948b48637fe7e10dcfc6d1256319a7b5ce4202cb54dfef8e37e7 430.8 μs 301.7 μs 1.43
marlowe-semantics/4c3efd13b6c69112a8a888372d56c86e60c232125976f29b1c3e21d9f537845c 1477 μs 1031 μs 1.43
marlowe-semantics/4d7adf91bfc93cebe95a7e054ec17cfbb912b32bd8aecb48a228b50e02b055c8 1008.9999999999999 μs 699.9 μs 1.44
marlowe-semantics/4f9e8d361b85e62db2350dd3ae77463540e7af0d28e1eb68faeecc45f4655f57 579.6 μs 399.8 μs 1.45
marlowe-semantics/52df7c8dfaa5f801cd837faa65f2fd333665fff00a555ce8c55e36ddc003007a 523.6 μs 362.5 μs 1.44
marlowe-semantics/53ed4db7ab33d6f907eec91a861d1188269be5ae1892d07ee71161bfb55a7cb7 538.3 μs 372.3 μs 1.45
marlowe-semantics/55dfe42688ad683b638df1fa7700219f00f53b335a85a2825502ab1e0687197e 433.8 μs 301.3 μs 1.44
marlowe-semantics/56333d4e413dbf1a665463bf68067f63c118f38f7539b7ba7167d577c0c8b8ce 1117 μs 771.8 μs 1.45
marlowe-semantics/57728d8b19b0e06412786f3dfed9e1894cd0ad1d2bc2bd497ec0ecb68f989d2b 430.7 μs 301.2 μs 1.43
marlowe-semantics/5abae75af26f45658beccbe48f7c88e74efdfc0b8409ba1e98f95fa5b6caf999 710.9 μs 495.3 μs 1.44
marlowe-semantics/5d0a88250f13c49c20e146819357a808911c878a0e0a7d6f7fe1d4a619e06112 1511 μs 1048 μs 1.44
marlowe-semantics/5e274e0f593511543d41570a4b03646c1d7539062b5728182e073e5760561a66 1463 μs 1016.9999999999999 μs 1.44
marlowe-semantics/5e2c68ac9f62580d626636679679b97109109df7ac1a8ce86d3e43dfb5e4f6bc 744.1 μs 520.2 μs 1.43
marlowe-semantics/5f130d19918807b60eab4c03119d67878fb6c6712c28c54f5a25792049294acc 440.4 μs 306.9 μs 1.43
marlowe-semantics/5f306b4b24ff2b39dab6cdc9ac6ca9bb442c1dc6f4e7e412eeb5a3ced42fb642 1089 μs 759.7 μs 1.43
marlowe-semantics/5f3d46c57a56cef6764f96c9de9677ac6e494dd7a4e368d1c8dd9c1f7a4309a5 709.8 μs 491.7 μs 1.44
marlowe-semantics/64c3d5b43f005855ffc4d0950a02fd159aa1575294ea39061b81a194ebb9eaae 958.7 μs 667.6 μs 1.44
marlowe-semantics/65bc4b69b46d18fdff0fadbf00dd5ec2b3e03805fac9d5fb4ff2d3066e53fc7e 3411 μs 2299 μs 1.48
marlowe-semantics/66af9e473d75e3f464971f6879cc0f2ef84bafcb38fbfa1dbc31ac2053628a38 1794 μs 1241 μs 1.45
marlowe-semantics/675d63836cad11b547d1b4cddd498f04c919d4342612accf40913f9ae9419fac 1505 μs 1039 μs 1.45
marlowe-semantics/67ba5a9a0245ee3aff4f34852b9889b8c810fccd3dce2a23910bddd35c503b71 8101.000000000001 μs 5696 μs 1.42
marlowe-semantics/6d88f7294dd2b5ce02c3dc609bc7715bd508009738401d264bf9b3eb7c6f49c1 707.3 μs 491.3 μs 1.44
marlowe-semantics/70f65b21b77ddb451f3df9d9fb403ced3d10e1e953867cc4900cc25e5b9dec47 1140 μs 787.9 μs 1.45
marlowe-semantics/71965c9ccae31f1ffc1d85aa20a356d4ed97a420954018d8301ec4f9783be0d7 687.2 μs 476 μs 1.44
marlowe-semantics/74c67f2f182b9a0a66c62b95d6fac5ace3f7e71ea3abfc52ffbe3ecb93436ea2 1153 μs 797.4 μs 1.45
marlowe-semantics/7529b206a78becb793da74b78c04d9d33a2540a1abd79718e681228f4057403a 1154 μs 801.8 μs 1.44
marlowe-semantics/75a8bb183688bce447e00f435a144c835435e40a5defc6f3b9be68b70b4a3db6 1004.9999999999999 μs 696.7 μs 1.44
marlowe-semantics/7a758e17486d1a30462c32a5d5309bd1e98322a9dcbe277c143ed3aede9d265f 749.1 μs 516.3 μs 1.45
marlowe-semantics/7cbc5644b745f4ea635aca42cce5e4a4b9d2e61afdb3ac18128e1688c07071ba 685.6 μs 474.1 μs 1.45
marlowe-semantics/82213dfdb6a812b40446438767c61a388d2c0cfd0cbf7fd4a372b0dc59fa17e1 1859 μs 1283 μs 1.45
marlowe-semantics/8c7fdc3da6822b5112074380003524f50fb3a1ce6db4e501df1086773c6c0201 1661 μs 1159 μs 1.43
marlowe-semantics/8d9ae67656a2911ab15a8e5301c960c69aa2517055197aff6b60a87ff718d66c 512.9 μs 365.6 μs 1.40
marlowe-semantics/9fabc4fc3440cdb776b28c9bb1dd49c9a5b1605fe1490aa3f4f64a3fa8881b25 1511 μs 1050 μs 1.44
marlowe-semantics/a85173a832db3ea944fafc406dfe3fa3235254897d6d1d0e21bc380147687bd5 538.9 μs 373.3 μs 1.44
marlowe-semantics/a9a853b6d083551f4ed2995551af287880ef42aee239a2d9bc5314d127cce592 740.5 μs 517 μs 1.43
marlowe-semantics/acb9c83c2b78dabef8674319ad69ba54912cd9997bdf2d8b2998c6bfeef3b122 946.5 μs 660.7 μs 1.43
marlowe-semantics/ccab11ce1a8774135d0e3c9e635631b68af9e276b5dabc66ff669d5650d0be1c 1444 μs 1363 μs 1.06
marlowe-semantics/f339f59bdf92495ed2b14e2e4d3705972b4dda59aa929cffe0f1ff5355db8d79 6582 μs 6177 μs 1.07
marlowe-role-payout/159e5a1bf16fe984b5569be7011b61b5e98f5d2839ca7e1b34c7f2afc7ffb58e 245.1 μs 168.9 μs 1.45
marlowe-role-payout/195f522b596360690d04586a2563470f2214163435331a6622311f7323433f1c 239.8 μs 164.6 μs 1.46
marlowe-role-payout/1a20b465d48a585ffd622bd8dc26a498a3c12f930ab4feab3a5064cfb3bc536a 270.1 μs 187 μs 1.44
marlowe-role-payout/211e1b6c10260c4620074d2e372c260d38643a3d605f63772524034f0a4a7632 258 μs 181.2 μs 1.42
marlowe-role-payout/a0fba5740174b5cd24036c8b008cb1efde73f1edae097b9325c6117a0ff40d3b 270.3 μs 187.3 μs 1.44
marlowe-role-payout/a1b25347409c3993feca1a60b6fcaf93d1d4bbaae19ab06fdf50cedc26cee68d 235 μs 162.1 μs 1.45
marlowe-role-payout/a27524cfad019df45e4e8316f927346d4cc39da6bdd294fb2c33c3f58e6a8994 244.6 μs 168.1 μs 1.46
marlowe-role-payout/a6664a2d2a82f370a34a36a45234f6b33120a39372331678a3b3690312560ce9 296 μs 203.2 μs 1.46
marlowe-role-payout/a6f064b83b31032ea7f25921364727224707268e472a569f584cc6b1d8c017e8 245.6 μs 168.7 μs 1.46
marlowe-role-payout/a7cb09f417c3f089619fe25b7624392026382b458486129efcff18f8912bf302 244.3 μs 168 μs 1.45
marlowe-role-payout/a92b4072cb8601fa697e1150c08463b14ffced54eb963df08d322216e27373cb 245.2 μs 169.2 μs 1.45
marlowe-role-payout/af2e072b5adfaa7211e0b341e1f7319c4f4e7364a4247c9247132a927e914753 290.9 μs 201.3 μs 1.45
marlowe-role-payout/b43564af5f13cc5208b92b1ad6d45369446f378d3891e5cb3e353b30d4f3fb10 243.4 μs 168.5 μs 1.44
marlowe-role-payout/b6243a5b4c353ce4852aa41705111d57867d2783eeef76f6d59beb2360da6e90 326.9 μs 226.9 μs 1.44
marlowe-role-payout/b869f3928200061abb1c3060425b9354b0e08cbf4400b340b8707c14b34317cd 360.9 μs 250.6 μs 1.44
marlowe-role-payout/bcdbc576d63b0454100ad06893812edafc2e7e4934fec1b44e2d06eb34f36eb8 245.2 μs 169.1 μs 1.45
marlowe-role-payout/bd460b7549b70c52e37b312a4242041eac18fe4a266f018bcea0c78a9085a271 282.1 μs 195.3 μs 1.44
marlowe-role-payout/bd79f4a84db23b7c4cd219d498bd581e085cbc3437957e74a8862281a700700b 280.7 μs 194.4 μs 1.44
marlowe-role-payout/c11490431db3a92efdda70933ba411a0423935e73a75c856e326dbcf6672f3bf 247.3 μs 171.6 μs 1.44
marlowe-role-payout/c4d4c88c5fe378a25a034025994a0d0b1642f10c8e6e513f872327fa895bfc7e 262.8 μs 181.3 μs 1.45
marlowe-role-payout/c78eeba7681d2ab51b4758efa4c812cc041928837c6e7563d8283cce67ce2e02 263.2 μs 181.2 μs 1.45
marlowe-role-payout/c99ecc2146ce2066ba6dffc734923264f8794815acbc2ec74c2c2c42ba272e4d 301.1 μs 208.3 μs 1.45
marlowe-role-payout/caa409c40e39aed9b0f59214b4baa178c375526dea6026b4552b88d2cc729716 234.1 μs 162 μs 1.45
marlowe-role-payout/cb2ab8e22d1f64e8d204dece092e90e9bf1fa8b2a6e9cba5012dbe4978065832 247.4 μs 171.5 μs 1.44
marlowe-role-payout/cc1e82927f6c65b3e912200ae30588793d2066e1d4a6627c21955944ac9bd528 279.5 μs 193.9 μs 1.44
marlowe-role-payout/d5cda74eb0947e025e02fb8ed365df39d0a43e4b42cd3573ac2d8fcb29115997 273.4 μs 188.3 μs 1.45
marlowe-role-payout/d6bc8ac4155e22300085784148bbc9d9bbfea896e1009dd396610a90e3943032 282.1 μs 195.3 μs 1.44
marlowe-role-payout/da353bf9219801fa1bf703fc161497570954e9af7e10ffe95c911a9ef97e77bd 253.2 μs 175.8 μs 1.44
marlowe-role-payout/dc45c5f1b700b1334db99f50823321daaef0e6925b9b2fabbc9df7cde65af62e 255.1 μs 177.2 μs 1.44
marlowe-role-payout/eabeeae18131af89fa57936c0e9eb8d2c7adba534f7e1a517d75410028fa0d6c 244.7 μs 210.5 μs 1.16
marlowe-role-payout/ec4712ee820eb959a43ebedfab6735f2325fa52994747526ffd2a4f4f84dd58e 276.8 μs 191.1 μs 1.45
marlowe-role-payout/ee3962fbd7373360f46decef3c9bda536a0b1daf6cda3b8a4bcfd6deeb5b4c53 280 μs 193.4 μs 1.45
marlowe-role-payout/f1a1e6a487f91feca5606f72bbb1e948c71abf043c6a0ea83bfea9ec6a0f08d8 226.1 μs 189.5 μs 1.19

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

CC: @IntersectMBO/plutus-core

Please sign in to comment.