Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7b9e60b

Browse files
committed
Performance improblements
- Using foldl’ instead of foldl for `fsymbols`. - Using Data.Map.Strict instaed of lists for the lookup. - Turned the FunctionExtras, which are ignored when comparing into it’s own datatype, so that we can use derived Ord and Eq. - Types now have proper Ord and Eq instances, and the typeCompare was made explicit. (Faking Ord was stupid!)
1 parent d3e7409 commit 7b9e60b

File tree

9 files changed

+122
-115
lines changed

9 files changed

+122
-115
lines changed

‎data-bitcode-llvm.cabal‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ library
2525
src
2626
build-depends:
2727
base >= 4.7 && < 5
28+
, containers >= 0.5.7
2829
, pretty >= 1.1
2930
, data-bitcode >= 0.1
3031
, binary >= 0.8

‎src/Data/BitCode/LLVM/Classes/ToSymbols.hs‎

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# OPTIONS_GHC -fprof-auto #-}
12
{-# LANGUAGE RecordWildCards #-}
23
{-# LANGUAGE TypeSynonymInstances #-}
34
{-# LANGUAGE FlexibleInstances #-}
@@ -8,6 +9,7 @@ import Data.BitCode.LLVM.Value as V
89
import Data.BitCode.LLVM.Function as F
910
import Data.BitCode.LLVM.Instruction as I
1011
import Data.Maybe (catMaybes)
12+
import Data.Foldable (foldl')
1113

1214
-- I guess this should just be Foldable or Traversable.
1315

@@ -18,17 +20,17 @@ class ToSymbols a where
1820

1921
instance (ToSymbols a) => ToSymbols [a] where
2022
symbols = concatMap symbols
21-
fsymbols s_ = foldl fsymbols s_
23+
fsymbols s_ = foldl' fsymbols s_
2224

2325
instance ToSymbols Value where
2426
symbols V.Global{..} | Just s <- gInit = symbols s
25-
symbols V.Function{..} = concatMap symbols $ catMaybes [fPrologueData, fPrefixData]
27+
symbols V.Function{..} = concatMap symbols $ catMaybes [fePrologueData fExtra, fePrefixData fExtra]
2628
symbols V.Alias{..} = symbols aVal
2729
symbols V.Constant{..} = symbols cConst
2830
symbols _ = []
2931

3032
fsymbols s_ V.Global{..} | Just s <- gInit = fsymbols s_ s
31-
fsymbols s_ V.Function{..} = foldl fsymbols s_ (catMaybes [fPrologueData, fPrefixData])
33+
fsymbols s_ V.Function{..} = foldl' fsymbols s_ (catMaybes [fePrologueData fExtra, fePrefixData fExtra])
3234
fsymbols s_ V.Alias{..} = fsymbols s_ aVal
3335
fsymbols s_ V.Constant{..} = fsymbols s_ cConst
3436
fsymbols s_ _ = s_
@@ -41,12 +43,12 @@ instance ToSymbols Const where
4143
symbols (V.Cast _ _ s) = symbols s
4244
symbols (V.InboundsGep _ ss) = concatMap symbols ss
4345
symbols _ = []
44-
fsymbols s_ (V.Array ss) = foldl fsymbols s_ ss
45-
fsymbols s_ (V.Vector ss) = foldl fsymbols s_ ss
46-
fsymbols s_ (V.Struct ss) = foldl fsymbols s_ ss
47-
fsymbols s_ (V.BinOp _ s s') = foldl fsymbols s_ [s, s']
48-
fsymbols s_ (V.Cast _ _ s) = foldl fsymbols s_ [s]
49-
fsymbols s_ (V.InboundsGep _ ss) = foldl fsymbols s_ ss
46+
fsymbols s_ (V.Array ss) = foldl' fsymbols s_ ss
47+
fsymbols s_ (V.Vector ss) = foldl' fsymbols s_ ss
48+
fsymbols s_ (V.Struct ss) = foldl' fsymbols s_ ss
49+
fsymbols s_ (V.BinOp _ s s') = foldl' fsymbols s_ [s, s']
50+
fsymbols s_ (V.Cast _ _ s) = foldl' fsymbols s_ [s]
51+
fsymbols s_ (V.InboundsGep _ ss) = foldl' fsymbols s_ ss
5052
fsymbols s_ _ = s_
5153

5254

@@ -58,12 +60,12 @@ instance ToSymbols BlockInst where
5860

5961
instance ToSymbols BasicBlock where
6062
symbols (BasicBlock insts) = concatMap symbols insts
61-
fsymbols s_ (BasicBlock insts) = foldl fsymbols s_ insts
63+
fsymbols s_ (BasicBlock insts) = foldl' fsymbols s_ insts
6264

6365
instance ToSymbols Function where
6466
-- TODO: do we want to apply symbols on the result instead of only to const?
6567
symbols (F.Function sig const body) = symbols sig ++ concatMap symbols const ++ concatMap symbols body
66-
fsymbols s_ (F.Function sig const body) = foldl fsymbols (foldl fsymbols s_ (sig:const)) body
68+
fsymbols s_ (F.Function sig const body) = foldl' fsymbols (foldl' fsymbols s_ (sig:const)) body
6769

6870
instance ToSymbols Symbol where
6971
symbols s = s:symbols (symbolValue s)
@@ -94,19 +96,19 @@ instance ToSymbols Inst where
9496
fsymbols s_ (I.Alloca _ s _) = fsymbols s_ s
9597
fsymbols s_ (I.Cast _ _ s) = fsymbols s_ s
9698
fsymbols s_ (I.Load _ s _) = fsymbols s_ s
97-
fsymbols s_ (I.Store s s' _) = foldl fsymbols s_ [s,s']
98-
fsymbols s_ (I.Call _ _ _ s _ ss) = foldl fsymbols s_ (s:ss)
99-
fsymbols s_ (I.Cmp2 _ s s' _) = foldl fsymbols s_ [s,s']
100-
fsymbols s_ (I.Gep _ _ s ss) = foldl fsymbols s_ (s:ss)
99+
fsymbols s_ (I.Store s s' _) = foldl' fsymbols s_ [s,s']
100+
fsymbols s_ (I.Call _ _ _ s _ ss) = foldl' fsymbols s_ (s:ss)
101+
fsymbols s_ (I.Cmp2 _ s s' _) = foldl' fsymbols s_ [s,s']
102+
fsymbols s_ (I.Gep _ _ s ss) = foldl' fsymbols s_ (s:ss)
101103
fsymbols s_ (I.ExtractValue s _) = fsymbols s_ s
102104
fsymbols s_ (I.Ret (Just s)) = fsymbols s_ s
103105
fsymbols s_ (I.Ret Nothing) = s_
104106
fsymbols s_ (I.UBr _) = s_
105107
fsymbols s_ (I.Br s _ _) = fsymbols s_ s
106-
fsymbols s_ (I.BinOp _ _ l r _) = foldl fsymbols s_ [l, r]
107-
fsymbols s_ (I.Switch s _ sbs) = foldl fsymbols s_ (s:map fst sbs)
108-
fsymbols s_ (I.CmpXchg p c n _ _ _) = foldl fsymbols s_ [p, c, n]
108+
fsymbols s_ (I.BinOp _ _ l r _) = foldl' fsymbols s_ [l, r]
109+
fsymbols s_ (I.Switch s _ sbs) = foldl' fsymbols s_ (s:map fst sbs)
110+
fsymbols s_ (I.CmpXchg p c n _ _ _) = foldl' fsymbols s_ [p, c, n]
109111
fsymbols s_ (I.Fence _ _) = s_
110-
fsymbols s_ (I.AtomicRMW s s' _ _ _) = foldl fsymbols s_ [s, s']
111-
fsymbols s_ (I.AtomicStore s s' _ _ _) = foldl fsymbols s_ [s, s']
112+
fsymbols s_ (I.AtomicRMW s s' _ _ _) = foldl' fsymbols s_ [s, s']
113+
fsymbols s_ (I.AtomicStore s s' _ _ _) = foldl' fsymbols s_ [s, s']
112114
fsymbols s_ (I.AtomicLoad _ s _ _ _) = fsymbols s_ s

‎src/Data/BitCode/LLVM/FromBitCode.hs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,10 @@ parseFunctionDecl'
359359
ty <- askType tyId
360360
let prologueData = if prologueDataId /= 0 then Just (Unnamed (FwdRef (prologueDataId -1))) else Nothing
361361
prefixData = if prefixDataId /= 0 then Just (Unnamed (FwdRef (prefixDataId -1))) else Nothing
362-
tellValue $ V.Function (Ptr 0 ty) (toEnum' cconv) (isProto /=0) (toEnum' linkage)
362+
tellValue $ V.Function (Ptr 0 ty) (toEnum' cconv) (toEnum' linkage)
363363
paramAttrId alignment section (toEnum' visibility) gc
364-
(unnamedAddr /= 0) prologueData (toEnum' storageClass)
365-
comdat prefixData personality
364+
(unnamedAddr /= 0) (toEnum' storageClass)
365+
comdat personality (FE (isProto /=0) prologueData prefixData)
366366

367367
parseFunctionDecl' vs = fail $ "Failed to parse functiond decl from " ++ show (length vs) ++ " values " ++ show vs
368368

@@ -463,10 +463,10 @@ parseModule bs = do
463463

464464
trace "Parsing Decls"
465465

466-
let functionDefs = [f | f@(Named _ (V.Function {..})) <- values, not fIsProto] ++
467-
[f | f@(Unnamed (V.Function {..})) <- values, not fIsProto]
468-
functionDecl = [f | f@(Named _ (V.Function {..})) <- values, fIsProto ] ++
469-
[f | f@(Unnamed (V.Function {..})) <- values, fIsProto ]
466+
let functionDefs = [f | f@(Named _ (V.Function {..})) <- values, not (feProto fExtra)] ++
467+
[f | f@(Unnamed (V.Function {..})) <- values, not (feProto fExtra)]
468+
functionDecl = [f | f@(Named _ (V.Function {..})) <- values, feProto fExtra ] ++
469+
[f | f@(Unnamed (V.Function {..})) <- values, feProto fExtra ]
470470
(unless (length functionDefs == length functionBlocks)) $ fail $ "#functionDecls (" ++ show (length functionDefs) ++ ") does not match #functionBodies (" ++ show (length functionBlocks) ++ ")"
471471

472472
trace "Parsing Functions"

‎src/Data/BitCode/LLVM/Pretty.hs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
module Data.BitCode.LLVM.Pretty where
66

77
import Data.Word (Word64)
8+
import Data.Map.Strict (Map)
9+
import qualified Data.Map.Strict as Map
810
import Data.Maybe (fromMaybe)
911
import Data.Char (toLower)
1012
import Text.PrettyPrint
@@ -38,9 +40,12 @@ instance (Pretty a) => Pretty (Maybe a) where
3840
pretty (Just x) = pretty x
3941
pretty Nothing = empty
4042

43+
instance (Pretty a, Pretty b) => Pretty (Map a b) where
44+
pretty = pretty . Map.toList
45+
4146
prefix :: Value -> Doc
4247
prefix (Global{}) = char '@'
43-
prefix (V.Function{..}) | fIsProto = text "decl "
48+
prefix (V.Function{..}) | feProto fExtra = text "decl "
4449
| otherwise = text "def "
4550
prefix (Alias{..}) = char '~'
4651
prefix (Constant{}) = text "const "
@@ -63,7 +68,7 @@ suffix (V.Label t) = text "::" <+> pretty t
6368

6469
-- * Values
6570
instance Pretty Value where
66-
pretty v@(V.Function{..}) | Just prefixData <- fPrefixData = prefix v <> suffix v $+$ text "Prefix:" <+> pretty prefixData
71+
pretty v@(V.Function{..}) | Just prefixData <- fePrefixData fExtra = prefix v <> suffix v $+$ text "Prefix:" <+> pretty prefixData
6772
pretty v = prefix v <> suffix v
6873

6974
-- * Symbols

‎src/Data/BitCode/LLVM/ToBitCode.hs‎

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# OPTIONS_GHC -fprof-auto #-}
12
{-# LANGUAGE FlexibleInstances #-}
23
{-# LANGUAGE RecordWildCards #-}
34
module Data.BitCode.LLVM.ToBitCode where
@@ -7,8 +8,8 @@ import Data.BitCode.LLVM
78
import Data.BitCode.LLVM.Util
89
import Data.BitCode.LLVM.Function
910
import Data.BitCode.LLVM.Classes.HasType
10-
import qualified Data.BitCode.LLVM.Value as V (Const(..), Value(..), Named(..), Symbol, symbolValue)
11-
import qualified Data.BitCode.LLVM.Type as T (Ty(..), ftypes)
11+
import qualified Data.BitCode.LLVM.Value as V (Const(..), Value(..), Named(..), Symbol, symbolValue, FunctionExtra(..))
12+
import qualified Data.BitCode.LLVM.Type as T (Ty(..), ftypes, typeCompare)
1213
import qualified Data.BitCode.LLVM.Instruction as I (Inst(..), TailCallKind(..))
1314
import Data.BitCode.LLVM.Flags
1415

@@ -20,9 +21,11 @@ import qualified Data.BitCode.LLVM.Codes.Type as TC
2021
import qualified Data.BitCode.LLVM.Codes.Constants as CC
2122
import qualified Data.BitCode.LLVM.Codes.Function as FC
2223
import qualified Data.BitCode.LLVM.Codes.ValueSymtab as VST
24+
import qualified Data.Map.Strict as Map
25+
import Data.Map.Strict (Map)
2326

2427
import Data.BitCode.LLVM.Classes.ToSymbols
25-
import Data.List (elemIndex, sort, sortOn, groupBy, nub)
28+
import Data.List (elemIndex, sort, sortBy, groupBy, nub)
2629
import Data.Function (on)
2730

2831
import Data.Maybe (fromMaybe, catMaybes)
@@ -107,11 +110,18 @@ lookupValueIndex vs v = case elemIndex v vs of
107110
Just i -> fromIntegral i
108111
Nothing -> error . show $ text "Unable to find value" <+> pretty v <+> text "in" <+> pretty vs
109112

110-
lookupSymbolIndex :: (HasCallStack, Integral b) => [V.Symbol] -> V.Symbol -> b
111-
lookupSymbolIndex ss s = case elemIndex s ss of
113+
lookupSymbolIndex :: (HasCallStack, Integral b) => MapV.SymbolInt -> V.Symbol -> b
114+
lookupSymbolIndex ss s = case Map.lookup s ss of
112115
Just i -> fromIntegral i
113116
Nothing -> error . show $ text "Unable to find symbol" <+> pretty s <+> text "in" <+> pretty ss
114117

118+
--------------------------------------------------------------------------
119+
-- PP Utiltiys
120+
prettyIndexed :: (Pretty a) => [a] -> Doc
121+
prettyIndexed = pretty . zip ([0..] :: [Int])
122+
traceShowWith :: Show a => (b -> a) -> b -> b
123+
traceShowWith f x = traceShow (f x) x
124+
115125
-- We *can not* have ToNBitCode Module, as we
116126
-- need to know the position in the bitcode stream.
117127
-- And this includes the Indetification :(
@@ -145,12 +155,6 @@ instance ToNBitCode (Maybe Ident, Module) where
145155
-- , {- Block: SymTab 14 -}
146156
-- ]
147157
where
148-
--------------------------------------------------------------------------
149-
-- PP Utiltiys
150-
prettyIndexed :: (Pretty a) => [a] -> Doc
151-
prettyIndexed = pretty . zip ([0..] :: [Int])
152-
traceShowWith :: Show a => (b -> a) -> b -> b
153-
traceShowWith f x = traceShow (f x) x
154158
--------------------------------------------------------------------------
155159
-- Compute the offsets
156160
identBlock = toBitCode i
@@ -187,10 +191,10 @@ instance ToNBitCode (Maybe Ident, Module) where
187191
-- the constants table.
188192
--------------------------------------------------------------------------
189193
isDeclaration x
190-
| f@(V.Function{}) <- V.symbolValue x = V.fIsProto f
194+
| f@(V.Function{}) <- V.symbolValue x = V.feProto (V.fExtra f)
191195
| otherwise = False
192196
isFunction x
193-
| f@(V.Function{}) <- V.symbolValue x = not (V.fIsProto f)
197+
| f@(V.Function{}) <- V.symbolValue x = not (V.feProto (V.fExtra f))
194198
| otherwise = False
195199
isGlobal x
196200
| (V.Global{}) <- V.symbolValue x = True
@@ -220,7 +224,8 @@ instance ToNBitCode (Maybe Ident, Module) where
220224
--
221225
-- TODO: FLAGS: if -dump-valuelist:
222226
-- traceShowWith prettyIndexed $!
223-
valueList = globalSymbols ++ functionSymbols ++ constantSymbols
227+
valueList :: Map V.Symbol Int
228+
valueList = Map.fromList $ zip (globalSymbols ++ functionSymbols ++ constantSymbols) [0..]
224229

225230
-- * T Y P E S
226231
-- all top level types, and all the types to construct them. (e.g. i8** -> i8, i8*, and i8**).
@@ -236,7 +241,7 @@ instance ToNBitCode (Maybe Ident, Module) where
236241

237242
-- | Turn a set of Constant Values unto BitCode Records.
238243
mkConstBlock :: HasCallStack
239-
=> [V.Symbol] -- ^ values that can be referenced.
244+
=> MapV.SymbolInt -- ^ values that can be referenced.
240245
-> [V.Symbol] -- ^ the constants to turn into BitCode
241246
-> [NBitCode]
242247
mkConstBlock values consts | length consts > 0 = [ mkBlock CONSTANTS .
@@ -252,7 +257,7 @@ instance ToNBitCode (Maybe Ident, Module) where
252257
| (V.Constant t c) <- V.symbolValue s
253258
= (mkRec CC.CST_CODE_SETTYPE (lookupTypeIndex typeList t :: Int)):mkConstRec values c:map (mkConstRec values . V.cConst . V.symbolValue) cs
254259
| otherwise = error $ "Invalid constant " ++ show s
255-
mkConstRec :: HasCallStack => [V.Symbol] -> V.Const -> NBitCode
260+
mkConstRec :: HasCallStack => MapV.SymbolInt -> V.Const -> NBitCode
256261
mkConstRec constantSymbols V.Null = mkEmptyRec CC.CST_CODE_NULL
257262
mkConstRec constantSymbols V.Undef = mkEmptyRec CC.CST_CODE_UNDEF
258263
mkConstRec constantSymbols (V.Int n) = mkRec CC.CST_CODE_INTEGER (fromSigned n)
@@ -319,18 +324,18 @@ instance ToNBitCode (Maybe Ident, Module) where
319324
mkFunctionRec :: HasCallStack => V.Value -> NBitCode
320325
mkFunctionRec (V.Function{..}) = mkRec MC.FUNCTION [ lookupTypeIndex typeList t -- NOTE: Similar to Globals we store the pointee type.
321326
, fromEnum' fCallingConv
322-
, bool fIsProto
327+
, bool (V.feProto fExtra)
323328
, fromEnum' fLinkage
324329
, fParamAttrs
325330
, fAlignment
326331
, fSection
327332
, fromEnum' fVisibility
328333
, fGC
329334
, bool fUnnamedAddr
330-
, fromMaybe 0 ((+1) . lookupSymbolIndex valueList <$> fPrologueData)
335+
, fromMaybe 0 ((+1) . lookupSymbolIndex valueList <$> (V.fePrologueData fExtra))
331336
, fromEnum' fDLLStorageClass
332337
, fComdat
333-
, fromMaybe 0 ((+1) . lookupSymbolIndex valueList <$> fPrefixData)
338+
, fromMaybe 0 ((+1) . lookupSymbolIndex valueList <$> (V.fePrefixData fExtra))
334339
, fPersonalityFn
335340
]
336341
where (T.Ptr _ t) = fType
@@ -343,7 +348,7 @@ instance ToNBitCode (Maybe Ident, Module) where
343348
mkSymTabBlock syms = mkBlock VALUE_SYMTAB (catMaybes (map mkSymTabRec namedIdxdSyms))
344349
where namedIdxdSyms = [(idx, name, value) | (idx, (V.Named name value)) <- zip [0..] syms]
345350
mkSymTabRec :: (Int, String, V.Value) -> Maybe NBitCode
346-
mkSymTabRec (n, nm, (V.Function{..})) | fIsProto = Just (mkRec VST.VST_CODE_ENTRY (n:map fromEnum nm))
351+
mkSymTabRec (n, nm, (V.Function{..})) | V.feProto fExtra = Just (mkRec VST.VST_CODE_ENTRY (n:map fromEnum nm))
347352
-- LLVM 3.8 comes with FNENTRY, which has offset at the
348353
-- second position. This however requires computing the
349354
-- offset corret.
@@ -374,17 +379,23 @@ instance ToNBitCode (Maybe Ident, Module) where
374379
fArgs = map V.Unnamed $ zipWith V.Arg fArgTys [0..]
375380
-- function local constant
376381
fconstants :: [V.Symbol]
377-
fconstants = sortOn (V.cTy . V.symbolValue) (filter isConst consts)
382+
fconstants = sortBy (T.typeCompare `on` (V.cTy . V.symbolValue))
383+
-- ignore any constants that are available globally already
384+
. filter (\x -> not $ x `elem` constantSymbols)
385+
-- only constants
386+
. filter isConst
387+
$ consts
378388
isConst :: V.Symbol -> Bool
379389
isConst c | (V.Constant{}) <- V.symbolValue c = True
380390
| otherwise = False
381391
-- the values the body can reference.
382-
bodyVals:: [V.Symbol]
392+
383393
-- globals, functions, constants, (because we order them that way)
384394
-- plus fargs and fconstants per function body ontop of which are
385395
-- the references generated by the instructions will be placed.
386-
bodyVals = valueList ++ fArgs ++ fconstants
387-
nBodyVals = length bodyVals
396+
bodyVals :: Map V.Symbol Int
397+
bodyVals = Map.unionWith (error . show) valueList (Map.fromList $ zip (fArgs ++ fconstants) [(Map.size valueList)..])
398+
nBodyVals = Map.size bodyVals
388399

389400
blockInstructions :: HasCallStack => BasicBlock -> [I.Inst]
390401
blockInstructions (BasicBlock insts) = map snd insts
@@ -394,7 +405,8 @@ instance ToNBitCode (Maybe Ident, Module) where
394405
instVals = map V.Unnamed $ zipWith V.TRef [t | Just t <- map instTy (concatMap blockInstructions bbs)] [0..]
395406

396407
-- all values. This will be used to lookup indices for values in.
397-
allVals = bodyVals ++ instVals
408+
allVals :: Map V.Symbol Int
409+
allVals = Map.unionWith (error . show) bodyVals (Map.fromList $ zip instVals [(Map.size bodyVals)..])
398410

399411
-- These are in FromBitCode as well. TODO: Refactor move BitMasks into a common file.
400412
inAllocMask = shift (1 :: Int) 5
@@ -403,14 +415,14 @@ instance ToNBitCode (Maybe Ident, Module) where
403415

404416
-- Relative Symbol lookup
405417
lookupRelativeSymbolIndex :: (HasCallStack, Integral a)
406-
=> [V.Symbol] -- ^ values prior to entering the instruction block
418+
=> MapV.SymbolInt -- ^ values prior to entering the instruction block
407419
-> [V.Symbol] -- ^ instruction values
408420
-> Int -- ^ current instruction count
409421
-> V.Symbol -- ^ the symbol to lookup
410422
-> a
411423
lookupRelativeSymbolIndex vs ivs iN s = fromIntegral $ vN + iN - lookupSymbolIndex vals s
412-
where vN = length vs
413-
vals = vs ++ivs
424+
where vN = Map.size vs
425+
vals = Map.unionWith (error.show) vs (Map.fromList $zipivs [vN..])
414426

415427
lookupRelativeSymbolIndex' :: (HasCallStack, Integral a) => Int -> V.Symbol -> a
416428
lookupRelativeSymbolIndex' = lookupRelativeSymbolIndex bodyVals instVals

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /