diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3faf7b63..6c1b12b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: [push, pull_request] jobs: test_soms: + name: Run (Compiler=${{matrix.compiler}} GC=${{matrix.gc}} ${{matrix.cmake_flags}} runs-on: ubuntu-24.04 # continue-on-error: true strategy: @@ -11,10 +12,10 @@ jobs: matrix: compiler: [clang, gcc] gc: [GENERATIONAL, MARK_SWEEP, COPYING] - integers: + cmake_flags: - "-DUSE_TAGGING=true" - "-DUSE_TAGGING=false -DCACHE_INTEGER=true" - - "-DUSE_TAGGING=false -DCACHE_INTEGER=false" + - "-DUSE_TAGGING=false -DCACHE_INTEGER=false -DUSE_VECTOR_PRIMITIVES=false" steps: - name: Checkout SOM Repository @@ -26,6 +27,11 @@ jobs: run: | sudo apt-get install libcppunit-dev + # - name: Install PyTest + # run: | + # python3 -m pip install --upgrade pip + # python3 -m pip install pytest + - name: Install Clang 19 run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - @@ -49,26 +55,41 @@ jobs: export CXX=g++ fi echo $CC $CXX - echo cmake . -DGC_TYPE=${{ matrix.gc}} ${{ matrix.integers }} + echo cmake . -DGC_TYPE=${{ matrix.gc}} ${{ matrix.cmake_flags }} mkdir cmake-build cd cmake-build - cmake .. -DGC_TYPE=${{ matrix.gc}} ${{ matrix.integers }} -DCMAKE_BUILD_TYPE=Debug + cmake .. -DGC_TYPE=${{ matrix.gc}} ${{ matrix.cmake_flags }} -DCMAKE_BUILD_TYPE=Debug make -j5 - name: Run Unit Tests run: | cd cmake-build ./unittests -cp ../Smalltalk:../TestSuite/BasicInterpreterTests ../Examples/Hello.som + ./SOM++ -prim-hash-check -cp ../Smalltalk ../Examples/Benchmarks/BenchmarkHarness.som VectorBenchmark 1 1 + + # - name: Run Integration Tests + # run: | + # EXECUTABLE=./cmake-build/SOM++ CLASSPATH=./Smalltalk pytest - name: Run Tests on SOM VM run: | cd cmake-build ./SOM++ -cp ../Smalltalk ../TestSuite/TestHarness.som + - name: Integration Tests + run: | + python -m pip install --upgrade pip + pip install pytest + export VM=./cmake-build/SOM++ + export CLASSPATH=Smalltalk + export TEST_EXCEPTIONS=./integration-tests.yml + export AWFY=./core-lib/Examples/AreWeFastYet/Core + pytest core-lib/IntegrationTests + - name: Clang Tidy if: matrix.compiler == 'clang' run: | - clang-tidy-19 --config-file=.clang-tidy src/**/*.cpp -- -fdiagnostics-absolute-paths -DGC_TYPE=${{ matrix.gc}} ${{ matrix.integers }} -DUNITTESTS + clang-tidy-19 --config-file=.clang-tidy src/*.cpp src/**/*.cpp -- -fdiagnostics-absolute-paths -DGC_TYPE=${{ matrix.gc}} ${{ matrix.cmake_flags }} -DUNITTESTS - name: Clang Format if: matrix.compiler == 'clang' && matrix.gc == 'GENERATIONAL' diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2598c8b7..7adb7076 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,30 +23,32 @@ build_and_test: - yuria3 COMPILER: [clang] GC: [COPYING] - INTEGERS: + CMAKE_FLAGS: - "-DUSE_TAGGING=true" - "-DUSE_TAGGING=false -DCACHE_INTEGER=true" - "-DUSE_TAGGING=false -DCACHE_INTEGER=false" + - "-DUSE_TAGGING=false -DCACHE_INTEGER=false -DUSE_VECTOR_PRIMITIVES=false" # Track GCC only for one configuration - MACHINE: [yuria, yuria2, yuria3] COMPILER: [gcc] GC: [COPYING] - INTEGERS: ["-DUSE_TAGGING=true"] + CMAKE_FLAGS: ["-DUSE_TAGGING=true"] - MACHINE: [zullie1] COMPILER: [clang] GC: [GENERATIONAL] - INTEGERS: + CMAKE_FLAGS: - "-DUSE_TAGGING=true" - "-DUSE_TAGGING=false -DCACHE_INTEGER=true" - "-DUSE_TAGGING=false -DCACHE_INTEGER=false" + - "-DUSE_TAGGING=false -DCACHE_INTEGER=false -DUSE_VECTOR_PRIMITIVES=false" # Mark-Sweep working, but not super exciting - MACHINE: [yuria, yuria2, yuria3] COMPILER: [gcc] GC: [MARK_SWEEP] - INTEGERS: ["-DUSE_TAGGING=true"] + CMAKE_FLAGS: ["-DUSE_TAGGING=true"] tags: [$MACHINE] script: @@ -59,27 +61,32 @@ build_and_test: # compose a name for this configuration - |+ export NAME="som-$COMPILER-$GC" - if [[ $INTEGERS =~ "USE_TAGGING=true" ]]; then + if [[ $CMAKE_FLAGS =~ "USE_TAGGING=true" ]]; then NAME="$NAME-inttag" else NAME="$NAME-intbox" fi - if [[ $INTEGERS =~ "CACHE_INTEGER=true" ]]; then + if [[ $CMAKE_FLAGS =~ "CACHE_INTEGER=true" ]]; then NAME="$NAME-intcache" fi + if [[ $CMAKE_FLAGS =~ "DUSE_VECTOR_PRIMITIVES=false" ]]; then + NAME="$NAME-somvec" + fi NAME=`echo "$NAME" | tr '[:upper:]' '[:lower:]'` - - cd release - - cmake .. $INTEGERS -DGC_TYPE=$GC -DCMAKE_BUILD_TYPE=Release + - cd debug + - cmake .. $CMAKE_FLAGS -DGC_TYPE=$GC -DCMAKE_BUILD_TYPE=Debug - make -j - - mv SOM++ ../$NAME + - ./SOM++ -cfg -cp ../Smalltalk ../TestSuite/TestHarness.som + - ./unittests -cfg -cp ../Smalltalk:../TestSuite/BasicInterpreterTests ../Examples/Hello.som + - ./SOM++ -prim-hash-check -cp ../Smalltalk ../Examples/Benchmarks/BenchmarkHarness.som VectorBenchmark 1 1 - cd .. - - cd debug - - cmake .. $INTEGERS -DGC_TYPE=$GC -DCMAKE_BUILD_TYPE=Debug + - cd release + - cmake .. $CMAKE_FLAGS -DGC_TYPE=$GC -DCMAKE_BUILD_TYPE=Release - make -j - - ./SOM++ -cp ../Smalltalk ../TestSuite/TestHarness.som - - ./unittests -cp ../Smalltalk:../TestSuite/BasicInterpreterTests ../Examples/Hello.som + - ./SOM++ -cfg -cp ../Smalltalk ../TestSuite/TestHarness.som + - mv SOM++ ../$NAME - cd .. - |+ @@ -87,7 +94,7 @@ build_and_test: if [ "$COMPILER" = "clang" ]; then # this is to load balance the SomSom testing # only when compiling with Clang, and only on one machine for each integer configuration - if [ "$MACHINE $INTEGERS" = "zullie1 -DUSE_TAGGING=true" ] || [ "$MACHINE $INTEGERS" = "yuria2 -DUSE_TAGGING=false -DCACHE_INTEGER=true" ] || [ "$MACHINE $INTEGERS" = "yuria3 -DUSE_TAGGING=false -DCACHE_INTEGER=false" ]; then + if [ "$MACHINE $CMAKE_FLAGS" = "zullie1 -DUSE_TAGGING=true" ] || [ "$MACHINE $CMAKE_FLAGS" = "yuria2 -DUSE_TAGGING=false -DCACHE_INTEGER=true" ] || [ "$MACHINE $CMAKE_FLAGS" = "yuria3 -DUSE_TAGGING=false -DCACHE_INTEGER=false" ]; then ./$NAME -cp Smalltalk:TestSuite:core-lib/SomSom/src/compiler:core-lib/SomSom/src/interpreter:core-lib/SomSom/src/primitives:core-lib/SomSom/src/vm:core-lib/SomSom/src/vmobjects core-lib/SomSom/tests/SomSomTests.som fi fi diff --git a/CMakeLists.txt b/CMakeLists.txt index af117b82..d0b0b940 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,8 @@ set(INT_CACHE_MIN_VALUE -5 CACHE STRING "Lower bound of cached integers") set(INT_CACHE_MAX_VALUE 100 CACHE STRING "Upper bound of cached integers") option(GENERATE_INTEGER_HISTOGRAM "Generate histogram of allocated integers" FALSE) +option(USE_VECTOR_PRIMITIVES "Use Vector primitives" TRUE) + option(GENERATE_ALLOCATION_STATISTICS "Generate allocation statistics" FALSE) option(LOG_RECEIVER_TYPES "Log types of receivers" FALSE) @@ -82,6 +84,11 @@ endif () if (GENERATE_INTEGER_HISTOGRAM) add_definitions(-DGENERATE_INTEGER_HISTOGRAM) endif () +if(USE_VECTOR_PRIMITIVES) + add_definitions(-DUSE_VECTOR_PRIMITIVES=true) +else () + add_definitions(-DUSE_VECTOR_PRIMITIVES=false) +endif () if (GENERATE_ALLOCATION_STATISTICS) add_definitions(-DGENERATE_ALLOCATION_STATISTICS) endif () diff --git a/SOM.xcodeproj/project.pbxproj b/SOM.xcodeproj/project.pbxproj index d297e7e1..9e083998 100644 --- a/SOM.xcodeproj/project.pbxproj +++ b/SOM.xcodeproj/project.pbxproj @@ -62,6 +62,9 @@ 0A1C98762C5526AD00735850 /* DebugCopyingCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A1C98752C5526AD00735850 /* DebugCopyingCollector.cpp */; }; 0A1C98772C5526AD00735850 /* DebugCopyingCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A1C98752C5526AD00735850 /* DebugCopyingCollector.cpp */; }; 0A32B7FE199D6143002825DF /* IntegerBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A32B7FC199D6143002825DF /* IntegerBox.cpp */; }; + 0A32D54E2E198C610019DDCD /* Murmur3Hash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A32D54D2E198C610019DDCD /* Murmur3Hash.cpp */; }; + 0A32D54F2E198C610019DDCD /* Murmur3Hash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A32D54D2E198C610019DDCD /* Murmur3Hash.cpp */; }; + 0A32D5552E198EC40019DDCD /* HashingTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A32D5542E198EC40019DDCD /* HashingTest.cpp */; }; 0A3A3C921A5D546D004CB03B /* Array.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F5203120FA6624C00E75857 /* Array.cpp */; }; 0A3A3C931A5D546D004CB03B /* Block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F5203160FA6624C00E75857 /* Block.cpp */; }; 0A3A3C941A5D546D004CB03B /* Class.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F5203180FA6624C00E75857 /* Class.cpp */; }; @@ -142,6 +145,10 @@ 0AB80AD42C392B78006B6419 /* Print.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD12C392B78006B6419 /* Print.cpp */; }; 0AB80AD82C394806006B6419 /* Globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD72C394806006B6419 /* Globals.cpp */; }; 0AB80AD92C394806006B6419 /* Globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD72C394806006B6419 /* Globals.cpp */; }; + 0AEFFE542E01A97E00CA4C4C /* Vector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AEFFE532E01A97E00CA4C4C /* Vector.cpp */; }; + 0AEFFE552E01A97E00CA4C4C /* Vector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AEFFE532E01A97E00CA4C4C /* Vector.cpp */; }; + 0AEFFE582E01A99300CA4C4C /* VMVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AEFFE572E01A99300CA4C4C /* VMVector.cpp */; }; + 0AEFFE592E01A99300CA4C4C /* VMVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AEFFE572E01A99300CA4C4C /* VMVector.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -220,6 +227,10 @@ 0A32B7FC199D6143002825DF /* IntegerBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntegerBox.cpp; sourceTree = ""; }; 0A32B7FD199D6143002825DF /* IntegerBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntegerBox.h; sourceTree = ""; }; 0A32B80119A12A03002825DF /* VMObjectBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VMObjectBase.h; sourceTree = ""; }; + 0A32D54C2E198C610019DDCD /* Murmur3Hash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Murmur3Hash.h; sourceTree = ""; }; + 0A32D54D2E198C610019DDCD /* Murmur3Hash.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Murmur3Hash.cpp; sourceTree = ""; }; + 0A32D5532E198EC40019DDCD /* HashingTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HashingTest.h; path = unitTests/HashingTest.h; sourceTree = ""; }; + 0A32D5542E198EC40019DDCD /* HashingTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HashingTest.cpp; path = unitTests/HashingTest.cpp; sourceTree = ""; }; 0A335F6B1832D64100D7CFC8 /* Method.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Method.cpp; sourceTree = ""; }; 0A335F6C1832D64100D7CFC8 /* Method.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Method.h; sourceTree = ""; }; 0A335F6F1832D65100D7CFC8 /* Primitive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Primitive.cpp; sourceTree = ""; }; @@ -251,7 +262,10 @@ 0AB80AD52C392BAD006B6419 /* Print.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Print.h; sourceTree = ""; }; 0AB80AD62C3947F7006B6419 /* Globals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Globals.h; sourceTree = ""; }; 0AB80AD72C394806006B6419 /* Globals.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Globals.cpp; sourceTree = ""; }; - 0AB80ADA2C39AC27006B6419 /* InterpreterLoop.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InterpreterLoop.h; sourceTree = ""; }; + 0AEFFE522E01A97E00CA4C4C /* Vector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Vector.h; sourceTree = ""; }; + 0AEFFE532E01A97E00CA4C4C /* Vector.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Vector.cpp; sourceTree = ""; }; + 0AEFFE562E01A99300CA4C4C /* VMVector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VMVector.h; sourceTree = ""; }; + 0AEFFE572E01A99300CA4C4C /* VMVector.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VMVector.cpp; sourceTree = ""; }; 3F5202F10FA6624C00E75857 /* BytecodeGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BytecodeGenerator.cpp; sourceTree = ""; }; 3F5202F20FA6624C00E75857 /* BytecodeGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeGenerator.h; sourceTree = ""; tabWidth = 4; }; 3F5202F30FA6624C00E75857 /* ClassGenerationContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClassGenerationContext.cpp; sourceTree = ""; }; @@ -304,7 +318,6 @@ 3F5203320FA6624C00E75857 /* Universe.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Universe.cpp; sourceTree = ""; }; 3F5203330FA6624C00E75857 /* Universe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Universe.h; sourceTree = ""; }; 3F5203350FA6624C00E75857 /* ObjectFormats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectFormats.h; sourceTree = ""; }; - 3F5203360FA6624C00E75857 /* PrimitiveRoutine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrimitiveRoutine.h; sourceTree = ""; }; 3F5203370FA6624C00E75857 /* Signature.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Signature.cpp; sourceTree = ""; tabWidth = 4; }; 3F5203380FA6624C00E75857 /* Signature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Signature.h; sourceTree = ""; }; 3F52033B0FA6624C00E75857 /* VMArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VMArray.cpp; sourceTree = ""; }; @@ -438,13 +451,6 @@ name = Products; sourceTree = ""; }; - 0A1886C71832BBC900A2CBCA /* SOM++ */ = { - isa = PBXGroup; - children = ( - ); - path = "SOM++"; - sourceTree = ""; - }; 0A67EA7A19ACD44000830E3B /* unittests */ = { isa = PBXGroup; children = ( @@ -452,6 +458,8 @@ 0A67EA7019ACD43A00830E3B /* CloneObjectsTest.cpp */, 0A67EA7119ACD43A00830E3B /* main.cpp */, 0A67EAA319ACE09700830E3B /* CloneObjectsTest.h */, + 0A32D5532E198EC40019DDCD /* HashingTest.h */, + 0A32D5542E198EC40019DDCD /* HashingTest.cpp */, 0A67EAA519ACE09700830E3B /* WalkObjectsTest.h */, 0A67EAA619ACE09700830E3B /* WriteBarrierTest.h */, 0A67EA7319ACD43A00830E3B /* WalkObjectsTest.cpp */, @@ -479,7 +487,6 @@ E18AE29C0FB1C45E00EE6540 /* Examples */, E18AE27F0FB1C44700EE6540 /* Smalltalk */, 3F5202EF0FA6624C00E75857 /* src */, - 0A1886C71832BBC900A2CBCA /* SOM++ */, 0A1886C61832BBC900A2CBCA /* Products */, ); sourceTree = ""; @@ -533,7 +540,6 @@ 3F5203020FA6624C00E75857 /* bytecodes.h */, 3F5203030FA6624C00E75857 /* Interpreter.cpp */, 3F5203040FA6624C00E75857 /* Interpreter.h */, - 0AB80ADA2C39AC27006B6419 /* InterpreterLoop.h */, ); path = interpreter; sourceTree = ""; @@ -570,6 +576,8 @@ 3F52030C0FA6624C00E75857 /* debug.h */, 3F52030D0FA6624C00E75857 /* defs.h */, 3F52030F0FA6624C00E75857 /* gettimeofday.h */, + 0A32D54C2E198C610019DDCD /* Murmur3Hash.h */, + 0A32D54D2E198C610019DDCD /* Murmur3Hash.cpp */, 0A1887371832C12E00A2CBCA /* Timer.cpp */, 0A1887381832C12E00A2CBCA /* Timer.h */, 0A707528297DF36F00EB9F59 /* ParseInteger.h */, @@ -606,6 +614,8 @@ 3F5203250FA6624C00E75857 /* Symbol.h */, 3F5203260FA6624C00E75857 /* System.cpp */, 3F5203270FA6624C00E75857 /* System.h */, + 0AEFFE522E01A97E00CA4C4C /* Vector.h */, + 0AEFFE532E01A97E00CA4C4C /* Vector.cpp */, ); path = primitives; sourceTree = ""; @@ -649,7 +659,6 @@ 0A1887091832C0E400A2CBCA /* AbstractObject.cpp */, 0A18870A1832C0E400A2CBCA /* AbstractObject.h */, 3F5203350FA6624C00E75857 /* ObjectFormats.h */, - 3F5203360FA6624C00E75857 /* PrimitiveRoutine.h */, 0A32B7FC199D6143002825DF /* IntegerBox.cpp */, 0A32B7FD199D6143002825DF /* IntegerBox.h */, 3F5203370FA6624C00E75857 /* Signature.cpp */, @@ -685,6 +694,8 @@ 0A5A7E902C5D45A00011C783 /* VMSafePrimitive.cpp */, 0A5A7E942C602E8C0011C783 /* VMTrivialMethod.h */, 0A5A7E952C60F5BB0011C783 /* VMTrivialMethod.cpp */, + 0AEFFE562E01A99300CA4C4C /* VMVector.h */, + 0AEFFE572E01A99300CA4C4C /* VMVector.cpp */, ); path = vmobjects; sourceTree = ""; @@ -907,6 +918,7 @@ 0A1887351832C10E00A2CBCA /* MarkSweepCollector.cpp in Sources */, 0A5A7E912C5D45A00011C783 /* VMSafePrimitive.cpp in Sources */, 0A1886FB1832BCF500A2CBCA /* VMBlock.cpp in Sources */, + 0AEFFE592E01A99300CA4C4C /* VMVector.cpp in Sources */, 0A1887391832C12E00A2CBCA /* Timer.cpp in Sources */, 0A1887021832BCFA00A2CBCA /* VMMethod.cpp in Sources */, 0AB80AD32C392B78006B6419 /* Print.cpp in Sources */, @@ -931,6 +943,7 @@ 0A1887051832BCFA00A2CBCA /* VMString.cpp in Sources */, 0A3A3C9A1A5D546D004CB03B /* String.cpp in Sources */, 0A3A3C951A5D546D004CB03B /* Double.cpp in Sources */, + 0AEFFE552E01A97E00CA4C4C /* Vector.cpp in Sources */, 0A1C986E2C4F1D3900735850 /* debug.cpp in Sources */, 0A1886F61832BCED00A2CBCA /* Universe.cpp in Sources */, 0AB80ACF2C392811006B6419 /* IsValidObject.cpp in Sources */, @@ -950,6 +963,7 @@ 0A1886FE1832BCF500A2CBCA /* VMEvaluationPrimitive.cpp in Sources */, 0A1887311832C10E00A2CBCA /* CopyingCollector.cpp in Sources */, 0A1886DE1832BCC800A2CBCA /* Disassembler.cpp in Sources */, + 0A32D54F2E198C610019DDCD /* Murmur3Hash.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -957,6 +971,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0A32D5552E198EC40019DDCD /* HashingTest.cpp in Sources */, 0A3A3CAB1A5D546D004CB03B /* System.cpp in Sources */, 0A3A3CA31A5D546D004CB03B /* Class.cpp in Sources */, 0A67EA9019ACD83200830E3B /* Lexer.cpp in Sources */, @@ -976,8 +991,10 @@ 0A3A3CAA1A5D546D004CB03B /* Symbol.cpp in Sources */, 0A67EA8619ACD74800830E3B /* VMMethod.cpp in Sources */, 0A67EA7519ACD43A00830E3B /* CloneObjectsTest.cpp in Sources */, + 0AEFFE582E01A99300CA4C4C /* VMVector.cpp in Sources */, 0A67EA7919ACD43A00830E3B /* WriteBarrierTest.cpp in Sources */, 0A67EA9819ACD85300830E3B /* GenerationalCollector.cpp in Sources */, + 0A32D54E2E198C610019DDCD /* Murmur3Hash.cpp in Sources */, 0A67EA7B19ACD72C00830E3B /* VMInvokable.cpp in Sources */, 0A67EA9119ACD83200830E3B /* MethodGenerationContext.cpp in Sources */, 0A3A3CA61A5D546D004CB03B /* Method.cpp in Sources */, @@ -1007,6 +1024,7 @@ 0A67EA9619ACD84300830E3B /* Timer.cpp in Sources */, 0A67EA8D19ACD83200830E3B /* BytecodeGenerator.cpp in Sources */, 0A1C98612C43D6E200735850 /* Variable.cpp in Sources */, + 0AEFFE542E01A97E00CA4C4C /* Vector.cpp in Sources */, 0A67EA7619ACD43A00830E3B /* main.cpp in Sources */, 0A67EA8819ACD74800830E3B /* VMPrimitive.cpp in Sources */, 0A3A3CA21A5D546D004CB03B /* Block.cpp in Sources */, diff --git a/core-lib b/core-lib index 7bf0413d..bc2d1ce9 160000 --- a/core-lib +++ b/core-lib @@ -1 +1 @@ -Subproject commit 7bf0413d9e90e0484f5bde24f44b654c3672da24 +Subproject commit bc2d1ce9672f61f3d7581ac5938527f7a61eba6f diff --git a/integration-tests.yml b/integration-tests.yml new file mode 100644 index 00000000..8f448665 --- /dev/null +++ b/integration-tests.yml @@ -0,0 +1,166 @@ +known_failures: [] + + +failing_as_unspecified: + - Tests/arbint_double_div_err.som + - Tests/arbint_double_div_zero_err.som + - Tests/arbint_modulus_err.som + - Tests/array_at_idx0_err.som + - Tests/array_at_idx2_err.som + - Tests/array_at_idx_err.som + - Tests/array_at_negative_idx_err.som + - Tests/array_at_put_idx0_err.som + - Tests/array_at_put_idx2_err.som + - Tests/call2.som + + # I think this one is about E vs e, but maybe also double rendering and e+59 or e59 + - Tests/double2.som + - Tests/double_double_div.som + - Tests/int_double_div.som + + # printing is not specified + # 18446744073709552000 + # or 1.8446744073709552e19 are both valid options + - Tests/integer_asdouble.som + - Tests/sin.som + - Tests/cos.som + + + # I think IEEE allows for infinities here, and we probably want that + - Tests/double1.som + - Tests/double3.som + - Tests/double4.som + - Tests/double5.som + - Tests/double6.som + - Tests/double7.som + - Tests/double8.som + - Tests/double9.som + - Tests/double11.som + - Tests/double12.som + - Tests/double13.som + + # Java seems to do some rounding in the transition, but it's also requiring bigints + - Tests/double_asinteger.som + + + - Tests/fromstring.som + - Tests/fromstring_double.som + + + - Tests/double_double_div_err.som + - Tests/double_double_div_zero_err1.som + - Tests/double_double_div_zero_err2.som + - Tests/double_double_div_zero_err3.som + - Tests/double_double_div_zero_err4.som + - Tests/double_modulus.som + - Tests/double_modulus_err.som + + - Tests/exit_double.som + - Tests/exit_int_too_big.som + - Tests/exit_string.som + + - Tests/fromstring_double_err.som + - Tests/fromstring_err.som + + # - Tests/hashcode2.som + + - Tests/inst_var_at_bad_idx.som + - Tests/inst_var_at_put_bad_idx.som + + - Tests/instance_fields_overlap/test.som + - Tests/instance_fields_overlap2.som + + - Tests/ic1.som + + - Tests/int3.som + - Tests/int4.som + - Tests/int5.som + + - Tests/int5.som + - Tests/int8.som + - Tests/int9.som + + - Tests/int10.som + - Tests/int11.som + - Tests/int12.som + - Tests/int13.som + - Tests/int14.som + - Tests/int15.som + - Tests/int16.som + - Tests/int17.som + - Tests/int19.som + + # too large shifts would take too much memory to support + # need to specify this some how + - Tests/int20.som + - Tests/int21.som + - Tests/int22.som + - Tests/int23.som + - Tests/int24.som + - Tests/int25.som + - Tests/int27.som + - Tests/int28.som + - Tests/int30.som + - Tests/int31.som + - Tests/int32.som + + - Tests/int_double_div_err.som + - Tests/int_double_div_zero_err.som + - Tests/int_modulus.som + - Tests/int_modulus_err.som + + - Tests/load_string.som + + - Tests/mutate_methods.som + - Tests/mutate_superclass_method/test.som + + - Tests/nested_backtrace1.som + - Tests/nested_backtrace2.som + + - Tests/obj2.som + + - Tests/array_literals.som + - Tests/perform_string.som + - Tests/perform_witharguments_wrong.som + - Tests/perform_unknown.som + + - Tests/remainder_zero.som + - Tests/round.som + + - Tests/shift_right.som + - Tests/shift_right_too_big.som + - Tests/shift_right_type_err.som + + - Tests/str_escape_unknown.som + + - Tests/system2.som + - Tests/system_global_lookup_string.som + - Tests/system_global_put_string.som + + - Tests/test_literals_limit_1.som + - Tests/unknown_field_write.som + + # This should be specified so that the "set" of fields cannot be changed reflectively + # they obtained array can be changed, but it is expected to have no effect. + - Tests/mutate_fields.som + + # specify true, false, nil as literals not globals + - Tests/system_global.som + + # utf8 support required + - Tests/string_length.som + - Tests/is_letters.som + +unsupported: + # JS doesn't have a sqrt implementation for BigIntegers + - Tests/int26.som + - Tests/int29.som + + # som++ specific + - Tests/to32bits.som + - Tests/binary_super.som + + + +do_not_run: + - Tests/case_insensitive.som diff --git a/rebench.conf b/rebench.conf index fbee5696..be191af6 100644 --- a/rebench.conf +++ b/rebench.conf @@ -128,12 +128,14 @@ executors: som-clang-generational-inttag: {path: ., executable: som-clang-generational-inttag } som-clang-generational-intbox: {path: ., executable: som-clang-generational-intbox } som-clang-generational-intbox-intcache: {path: ., executable: som-clang-generational-intbox-intcache} + som-clang-generational-intbox-somvec: {path: ., executable: som-clang-generational-intbox-somvec } som-clang-mark_sweep-inttag: {path: ., executable: som-clang-mark_sweep-inttag } som-clang-mark_sweep-intbox: {path: ., executable: som-clang-mark_sweep-intbox } som-clang-mark_sweep-intbox-intcache: {path: ., executable: som-clang-mark_sweep-intbox-intcache } som-clang-copying-inttag: {path: ., executable: som-clang-copying-inttag } som-clang-copying-intbox: {path: ., executable: som-clang-copying-intbox } som-clang-copying-intbox-intcache: {path: ., executable: som-clang-copying-intbox-intcache } + som-clang-copying-intbox-somvec: {path: ., executable: som-clang-copying-intbox-somvec } # define the benchmarks to be executed for a re-executable benchmark run experiments: @@ -160,9 +162,11 @@ experiments: - som-clang-generational-inttag - som-clang-generational-intbox - som-clang-generational-intbox-intcache + - som-clang-generational-intbox-somvec - som-clang-mark_sweep-inttag - som-clang-mark_sweep-intbox - som-clang-mark_sweep-intbox-intcache - som-clang-copying-inttag - som-clang-copying-intbox - som-clang-copying-intbox-intcache + - som-clang-copying-intbox-somvec diff --git a/src/compiler/MethodGenerationContext.cpp b/src/compiler/MethodGenerationContext.cpp index 6bb59bcb..cfb61b90 100644 --- a/src/compiler/MethodGenerationContext.cpp +++ b/src/compiler/MethodGenerationContext.cpp @@ -284,7 +284,7 @@ VMTrivialMethod* MethodGenerationContext::assembleFieldSetter() { return MakeSetter(signature, arguments, fieldIndex, argIndex); } -VMPrimitive* MethodGenerationContext::AssemblePrimitive(bool classSide) { +VMInvokable* MethodGenerationContext::AssemblePrimitive(bool classSide) { return VMPrimitive::GetEmptyPrimitive(signature, classSide); } diff --git a/src/compiler/MethodGenerationContext.h b/src/compiler/MethodGenerationContext.h index 6a126bae..4b0b432d 100644 --- a/src/compiler/MethodGenerationContext.h +++ b/src/compiler/MethodGenerationContext.h @@ -49,7 +49,7 @@ class MethodGenerationContext { ~MethodGenerationContext(); VMInvokable* Assemble(); - VMPrimitive* AssemblePrimitive(bool classSide); + VMInvokable* AssemblePrimitive(bool classSide); int8_t FindLiteralIndex(vm_oop_t lit); bool FindVar(std::string& var, int64_t* index, int* context, diff --git a/src/misc/Murmur3Hash.cpp b/src/misc/Murmur3Hash.cpp new file mode 100644 index 00000000..0dbc33dd --- /dev/null +++ b/src/misc/Murmur3Hash.cpp @@ -0,0 +1,42 @@ +#include "Murmur3Hash.h" + +#include +#include + +static inline uint32_t murmur_32_scramble(uint32_t k) { + k *= 0xcc9e2d51; + k = (k << 15U) | (k >> 17U); + k *= 0x1b873593; + return k; +} + +// This implementation is taken from the Wikipedia page on MurmurHash +// https://en.wikipedia.org/w/index.php?title=MurmurHash&oldid=1295314879#Algorithm +uint32_t murmur3_32(const uint8_t* key, size_t len, uint32_t seed) { + uint32_t h = seed; + uint32_t k = 0; + + for (size_t i = len >> 2U; i != 0U; i--) { + std::memcpy(&k, key, sizeof(uint32_t)); + key += sizeof(uint32_t); + h ^= murmur_32_scramble(k); + h = (h << 13U) | (h >> 19U); + h = h * 5 + 0xe6546b64; + } + + k = 0; + for (size_t i = len & 3U; i != 0U; i--) { + k <<= 8U; + k |= key[i - 1]; + } + + h ^= murmur_32_scramble(k); + h ^= len; + h ^= h >> 16U; + h *= 0x85ebca6b; + h ^= h >> 13U; + h *= 0xc2b2ae35; + h ^= h >> 16U; + + return h; +} diff --git a/src/misc/Murmur3Hash.h b/src/misc/Murmur3Hash.h new file mode 100644 index 00000000..a013300c --- /dev/null +++ b/src/misc/Murmur3Hash.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +// Computes a 32-bit Murmur3 hash of the input key. +uint32_t murmur3_32(const uint8_t* key, size_t len, uint32_t seed); diff --git a/src/misc/defs.h b/src/misc/defs.h index 7c54e249..ac29996d 100644 --- a/src/misc/defs.h +++ b/src/misc/defs.h @@ -126,6 +126,13 @@ typedef DebugCopyingHeap HEAP_CLS; #define INT_CACHE_MAX_VALUE (100) #endif +// +// Vector Settings +// +#ifndef USE_VECTOR_PRIMITIVES + #define USE_VECTOR_PRIMITIVES true +#endif + // // Debugging // diff --git a/src/primitives/System.cpp b/src/primitives/System.cpp index da316ea5..ed39143d 100644 --- a/src/primitives/System.cpp +++ b/src/primitives/System.cpp @@ -53,8 +53,6 @@ #endif -static _System* System_; - static vm_oop_t sysGlobal_(vm_oop_t /*unused*/, vm_oop_t rightObj) { auto* arg = static_cast(rightObj); vm_oop_t result = Universe::GetGlobal(arg); diff --git a/src/primitives/Vector.cpp b/src/primitives/Vector.cpp new file mode 100644 index 00000000..a30bd639 --- /dev/null +++ b/src/primitives/Vector.cpp @@ -0,0 +1,127 @@ +#include "Vector.h" + +#include + +#include "../misc/defs.h" // NOLINT(misc-include-cleaner) +#include "../vm/Universe.h" +#include "../vmobjects/ObjectFormats.h" +#include "../vmobjects/VMFrame.h" +#include "../vmobjects/VMVector.h" + +static vm_oop_t vecNew(vm_oop_t clazz) { + auto* classPtr = static_cast(clazz); + int64_t const size = 50; + VMVector* vec = Universe::NewVector(size, classPtr); + return vec; +} + +static vm_oop_t vecNewSize(vm_oop_t clazz, vm_oop_t arg) { + int64_t const size = INT_VAL(arg); + return Universe::NewVector(size, static_cast(clazz)); +} + +static vm_oop_t vecAt(vm_oop_t obj, vm_oop_t arg) { + auto* self = static_cast(obj); + int64_t const index = INT_VAL(arg); + return self->GetStorage(index); +} + +static vm_oop_t vecAtAWFY(vm_oop_t obj, vm_oop_t arg) { + auto* self = static_cast(obj); + int64_t const index = INT_VAL(arg); + return self->AWFYGetStorage(index); +} + +static vm_oop_t vecAtPut(vm_oop_t obj, vm_oop_t at, vm_oop_t put) { + auto* self = static_cast(obj); // Cast itself as a VMVector + int64_t const index = INT_VAL(at); // Set the index looking for + // Call method to set the value at index. That deals with 1to0 indexing + // conversion + return self->SetStorage(index, put); +} + +static vm_oop_t vecAtPutAWFY(vm_oop_t obj, vm_oop_t at, vm_oop_t put) { + auto* self = static_cast(obj); // Cast itself as a VMVector + int64_t const index = INT_VAL(at); // Set the index looking for + // Call method to set the value at index. That does not deal with 1to0 + // indexing conversion + self->SetStorageAWFY(index, put); + return self; +} + +static vm_oop_t vecAppend(vm_oop_t obj, vm_oop_t arg) { + auto* self = static_cast(obj); + self->Append(arg); + return self; +} + +static vm_oop_t vecFirst(vm_oop_t obj) { + auto* self = static_cast(obj); + return self->GetFirst(); +} + +static vm_oop_t vecLast(vm_oop_t obj) { + auto* self = static_cast(obj); + return self->GetLast(); +} + +static vm_oop_t removeLast(vm_oop_t obj) { + auto* self = static_cast(obj); + return self->RemoveLast(); +} + +static vm_oop_t removeFirst(vm_oop_t obj) { + auto* self = static_cast(obj); + return self->RemoveFirst(); +} + +static vm_oop_t removeObject(vm_oop_t obj, vm_oop_t other) { + auto* self = static_cast(obj); + return self->RemoveObj(other); +} + +static vm_oop_t removeAll(vm_oop_t obj) { + auto* self = static_cast(obj); + self->RemoveAll(); + return self; +} + +static vm_oop_t vecSize(vm_oop_t obj) { + auto* self = static_cast(obj); + return self->Size(); +} + +static vm_oop_t capacity(vm_oop_t obj) { + auto* self = static_cast(obj); + return self->Capacity(); +} + +static vm_oop_t asArray(vm_oop_t obj) { + auto* self = static_cast(obj); + return self->copyStorageArray(); +} + +_Vector::_Vector() { + if (!USE_VECTOR_PRIMITIVES) { + return; + } + + // These bytecode hashes may need to be updated occasionally + // If they need to be updated, the corresponding primitive implementations + // likely need to be adapted, too. + // In order to print out hashes use the --hashes flag when executing SOM++ + + Add("at:", false, &vecAt, 315201797, &vecAtAWFY, 3362920797); + Add("at:put:", false, &vecAtPut, 315201797, &vecAtPutAWFY, 3362920797); + + Add("first", &vecFirst, false, 781539645); + Add("last", &vecLast, false, 997610644); + Add("append:", &vecAppend, false, 3383122912); + Add("remove", &removeLast, false, 146838394); + Add("remove:", &removeObject, false, 3385671007); + Add("size", &vecSize, false, 582059043); + Add("capacity", &capacity, false, 1600305580); + Add("asArray", &asArray, false, 2100819204); + Add("removeFirst", &removeFirst, false, 2083891304); + Add("removeAll", &removeAll, false, 1510429688); +}; diff --git a/src/primitives/Vector.h b/src/primitives/Vector.h new file mode 100644 index 00000000..3cb545e1 --- /dev/null +++ b/src/primitives/Vector.h @@ -0,0 +1,9 @@ +#pragma once + +#include "../primitivesCore/PrimitiveContainer.h" +#include "../vmobjects/ObjectFormats.h" + +class _Vector : public PrimitiveContainer { +public: + _Vector(); +}; diff --git a/src/primitivesCore/PrimitiveContainer.cpp b/src/primitivesCore/PrimitiveContainer.cpp index cdcab1db..67f100b2 100644 --- a/src/primitivesCore/PrimitiveContainer.cpp +++ b/src/primitivesCore/PrimitiveContainer.cpp @@ -27,12 +27,17 @@ #include "PrimitiveContainer.h" #include +#include #include #include #include +#include +#include "../vm/Print.h" #include "../vm/Symbols.h" +#include "../vm/Universe.h" #include "../vmobjects/VMClass.h" +#include "../vmobjects/VMInvokable.h" #include "../vmobjects/VMPrimitive.h" #include "../vmobjects/VMSafePrimitive.h" #include "../vmobjects/VMSymbol.h" @@ -42,87 +47,162 @@ void PrimitiveContainer::Add(const char* name, FramePrimitiveRoutine routine, bool classSide) { assert(framePrims.find(name) == framePrims.end()); - framePrims[std::string(name)] = {routine, classSide}; + framePrims[std::string(name)] = {FramePrim(routine, classSide, 0), + FramePrim()}; } void PrimitiveContainer::Add(const char* name, BinaryPrimitiveRoutine routine, bool classSide) { assert(binaryPrims.find(name) == binaryPrims.end()); - binaryPrims[std::string(name)] = {routine, classSide}; + binaryPrims[std::string(name)] = {BinaryPrim(routine, classSide, 0), + BinaryPrim()}; } void PrimitiveContainer::Add(const char* name, UnaryPrimitiveRoutine routine, bool classSide) { assert(unaryPrims.find(name) == unaryPrims.end()); - unaryPrims[std::string(name)] = {routine, classSide}; + unaryPrims[std::string(name)] = {UnaryPrim(routine, classSide, 0), + UnaryPrim()}; } void PrimitiveContainer::Add(const char* name, TernaryPrimitiveRoutine routine, bool classSide) { assert(ternaryPrims.find(name) == ternaryPrims.end()); - ternaryPrims[std::string(name)] = {routine, classSide}; + ternaryPrims[std::string(name)] = {TernaryPrim(routine, classSide, 0), + TernaryPrim()}; } -void PrimitiveContainer::InstallPrimitives(VMClass* clazz, bool classSide) { - for (auto const& p : unaryPrims) { - assert(p.second.IsValid()); - if (classSide != p.second.isClassSide) { - continue; - } +void PrimitiveContainer::Add(const char* name, FramePrimitiveRoutine routine, + bool classSide, size_t hash) { + assert(framePrims.find(name) == framePrims.end()); + framePrims[std::string(name)] = {FramePrim(routine, classSide, hash), + FramePrim()}; +} - VMSymbol* sig = SymbolFor(p.first); - if (clazz->AddInstanceInvokable( - VMSafePrimitive::GetSafeUnary(sig, p.second))) { - cout << "Warn: Primitive " << p.first - << " is not in class definition for class " - << clazz->GetName()->GetStdString() << '\n'; - } - } +void PrimitiveContainer::Add(const char* name, BinaryPrimitiveRoutine routine, + bool classSide, size_t hash) { + assert(binaryPrims.find(name) == binaryPrims.end()); + binaryPrims[std::string(name)] = {BinaryPrim(routine, classSide, hash), + BinaryPrim()}; +} + +void PrimitiveContainer::Add(const char* name, UnaryPrimitiveRoutine routine, + bool classSide, size_t hash) { + assert(unaryPrims.find(name) == unaryPrims.end()); + unaryPrims[std::string(name)] = {UnaryPrim(routine, classSide, hash), + UnaryPrim()}; +} + +void PrimitiveContainer::Add(const char* name, TernaryPrimitiveRoutine routine, + bool classSide, size_t hash) { + assert(ternaryPrims.find(name) == ternaryPrims.end()); + ternaryPrims[std::string(name)] = {TernaryPrim(routine, classSide, hash), + TernaryPrim()}; +} + +void PrimitiveContainer::Add(const char* name, bool classSide, + FramePrimitiveRoutine routine1, size_t hash1, + FramePrimitiveRoutine routine2, size_t hash2) { + assert(framePrims.find(name) == framePrims.end()); + framePrims[std::string(name)] = {FramePrim(routine1, classSide, hash1), + FramePrim(routine2, classSide, hash2)}; +} + +void PrimitiveContainer::Add(const char* name, bool classSide, + BinaryPrimitiveRoutine routine1, size_t hash1, + BinaryPrimitiveRoutine routine2, size_t hash2) { + assert(binaryPrims.find(name) == binaryPrims.end()); + binaryPrims[std::string(name)] = {BinaryPrim(routine1, classSide, hash1), + BinaryPrim(routine2, classSide, hash2)}; +} - for (auto const& p : binaryPrims) { - assert(p.second.IsValid()); - if (classSide != p.second.isClassSide) { +void PrimitiveContainer::Add(const char* name, bool classSide, + UnaryPrimitiveRoutine routine1, size_t hash1, + UnaryPrimitiveRoutine routine2, size_t hash2) { + assert(unaryPrims.find(name) == unaryPrims.end()); + unaryPrims[std::string(name)] = {UnaryPrim(routine1, classSide, hash1), + UnaryPrim(routine2, classSide, hash2)}; +} + +void PrimitiveContainer::Add(const char* name, bool classSide, + TernaryPrimitiveRoutine routine1, size_t hash1, + TernaryPrimitiveRoutine routine2, size_t hash2) { + assert(ternaryPrims.find(name) == ternaryPrims.end()); + ternaryPrims[std::string(name)] = {TernaryPrim(routine1, classSide, hash1), + TernaryPrim(routine2, classSide, hash2)}; +} + +template +bool PrimitiveContainer::installPrimitives( + bool classSide, bool showWarning, VMClass* clazz, + std::map>& prims, + VMInvokable* (*makePrimFn)(VMSymbol* sig, PrimT)) { + bool hasHashMismatch = false; + + for (auto const& p : prims) { + PrimT prim1 = std::get<0>(p.second); + PrimT prim2 = std::get<1>(p.second); + + assert(prim1.IsValid()); + if (classSide != prim1.isClassSide) { continue; } VMSymbol* sig = SymbolFor(p.first); - if (clazz->AddInstanceInvokable( - VMSafePrimitive::GetSafeBinary(sig, p.second))) { - cout << "Warn: Primitive " << p.first - << " is not in class definition for class " - << clazz->GetName()->GetStdString() << '\n'; - } - } - for (auto const& p : ternaryPrims) { - assert(p.second.IsValid()); - if (classSide != p.second.isClassSide) { - continue; + PrimInstallResult const result = clazz->InstallPrimitive( + makePrimFn(sig, prim1), prim1.bytecodeHash, !prim2.IsValid()); + if (!prim2.IsValid()) { + hasHashMismatch = + hasHashMismatch || result == PrimInstallResult::HASH_MISMATCH; } - VMSymbol* sig = SymbolFor(p.first); - if (clazz->AddInstanceInvokable( - VMSafePrimitive::GetSafeTernary(sig, p.second))) { + if (result == PrimInstallResult::INSTALLED_ADDED && showWarning) { cout << "Warn: Primitive " << p.first << " is not in class definition for class " << clazz->GetName()->GetStdString() << '\n'; + } else if (result == PrimInstallResult::HASH_MISMATCH && + prim2.IsValid()) { + assert(prim1.isClassSide == prim2.isClassSide); + PrimInstallResult const result2 = clazz->InstallPrimitive( + makePrimFn(sig, prim2), prim2.bytecodeHash, true); + hasHashMismatch = + hasHashMismatch || result2 == PrimInstallResult::HASH_MISMATCH; } } - for (auto const& p : framePrims) { - assert(p.second.IsValid()); - if (classSide != p.second.isClassSide) { - continue; - } + return hasHashMismatch; +} - VMSymbol* sig = SymbolFor(p.first); - if (clazz->AddInstanceInvokable( - VMPrimitive::GetFramePrim(sig, p.second))) { - cout << "Warn: Primitive " << p.first - << " is not in class definition for class " - << clazz->GetName()->GetStdString() << '\n'; - } +void PrimitiveContainer::InstallPrimitives(VMClass* clazz, bool classSide, + bool showWarning) { + bool hasHashMismatch = false; + hasHashMismatch = + hasHashMismatch || + installPrimitives(classSide, showWarning, clazz, unaryPrims, + VMSafePrimitive::GetSafeUnary); + hasHashMismatch = + hasHashMismatch || + installPrimitives(classSide, showWarning, clazz, binaryPrims, + VMSafePrimitive::GetSafeBinary); + hasHashMismatch = + hasHashMismatch || + installPrimitives(classSide, showWarning, clazz, ternaryPrims, + VMSafePrimitive::GetSafeTernary); + hasHashMismatch = hasHashMismatch || + installPrimitives(classSide, showWarning, clazz, + framePrims, VMPrimitive::GetFramePrim); + + if (abortOnCoreLibHashMismatch && hasHashMismatch) { + ErrorPrint("The implementation of methods in " + + clazz->GetName()->GetStdString() + + " seem to have changed.\n"); + ErrorPrint( + "The primitive implementation in the matching VM class may need to " + "be changed. See for instance _Vector::_Vector() in " + "primitives/Vector.cpp\n"); + Quit(1); } } diff --git a/src/primitivesCore/PrimitiveContainer.h b/src/primitivesCore/PrimitiveContainer.h index 497f0627..d4dae5ba 100644 --- a/src/primitivesCore/PrimitiveContainer.h +++ b/src/primitivesCore/PrimitiveContainer.h @@ -26,6 +26,7 @@ THE SOFTWARE. */ +#include #include #include "../misc/defs.h" @@ -40,7 +41,7 @@ class PrimitiveContainer { PrimitiveContainer() = default; virtual ~PrimitiveContainer() = default; - void InstallPrimitives(VMClass* clazz, bool classSide); + void InstallPrimitives(VMClass* clazz, bool classSide, bool showWarning); void Add(const char* name, FramePrimitiveRoutine /*routine*/, bool classSide); @@ -51,9 +52,37 @@ class PrimitiveContainer { void Add(const char* name, TernaryPrimitiveRoutine /*routine*/, bool classSide); + void Add(const char* name, FramePrimitiveRoutine /*routine*/, + bool classSide, size_t hash); + void Add(const char* name, UnaryPrimitiveRoutine /*routine*/, + bool classSide, size_t hash); + void Add(const char* name, BinaryPrimitiveRoutine /*routine*/, + bool classSide, size_t hash); + void Add(const char* name, TernaryPrimitiveRoutine /*routine*/, + bool classSide, size_t hash); + + void Add(const char* name, bool classSide, + FramePrimitiveRoutine /*routine*/, size_t hash1, + FramePrimitiveRoutine /*routine*/, size_t hash2); + void Add(const char* name, bool classSide, + UnaryPrimitiveRoutine /*routine*/, size_t hash1, + UnaryPrimitiveRoutine /*routine*/, size_t hash2); + void Add(const char* name, bool classSide, + BinaryPrimitiveRoutine /*routine*/, size_t hash1, + BinaryPrimitiveRoutine /*routine*/, size_t hash2); + void Add(const char* name, bool classSide, + TernaryPrimitiveRoutine /*routine*/, size_t hash1, + TernaryPrimitiveRoutine /*routine*/, size_t hash2); + private: - std::map framePrims; - std::map unaryPrims; - std::map binaryPrims; - std::map ternaryPrims; + std::map> framePrims; + std::map> unaryPrims; + std::map> binaryPrims; + std::map> ternaryPrims; + + template + bool installPrimitives( + bool classSide, bool showWarning, VMClass* clazz, + std::map>& prims, + VMInvokable* (*makePrimFn)(VMSymbol* sig, PrimT)); }; diff --git a/src/primitivesCore/PrimitiveLoader.cpp b/src/primitivesCore/PrimitiveLoader.cpp index 18d275fc..4193e1ca 100644 --- a/src/primitivesCore/PrimitiveLoader.cpp +++ b/src/primitivesCore/PrimitiveLoader.cpp @@ -40,13 +40,19 @@ #include "../primitives/String.h" #include "../primitives/Symbol.h" #include "../primitives/System.h" +#include "../primitives/Vector.h" #include "../vmobjects/ObjectFormats.h" #include "PrimitiveContainer.h" PrimitiveLoader PrimitiveLoader::loader; +PrimitiveLoader* PrimitiveLoader::GetInstance() { + return &loader; +} + PrimitiveLoader::PrimitiveLoader() { AddPrimitiveObject("Array", new _Array()); + AddPrimitiveObject("Vector", new _Vector()); AddPrimitiveObject("Block", new _Block()); AddPrimitiveObject("Class", new _Class()); AddPrimitiveObject("Double", new _Double()); @@ -65,6 +71,14 @@ PrimitiveLoader::~PrimitiveLoader() { } } +PrimitiveContainer* PrimitiveLoader::GetObject(const std::string& name) { + auto it = primitiveObjects.find(name); + if (it != primitiveObjects.end()) { + return it->second; + } + return nullptr; +} + void PrimitiveLoader::AddPrimitiveObject(const std::string& name, PrimitiveContainer* prim) { primitiveObjects[name] = prim; @@ -80,16 +94,18 @@ bool PrimitiveLoader::SupportsClass(const std::string& name) { void PrimitiveLoader::InstallPrimitives(const std::string& cname, VMClass* clazz, - bool classSide) { - loader.installPrimitives(cname, clazz, classSide); + bool classSide, + bool showWarning) { + loader.installPrimitives(cname, clazz, classSide, showWarning); } void PrimitiveLoader::installPrimitives(const std::string& cname, VMClass* clazz, - bool classSide) { + bool classSide, + bool showWarning) { if (primitiveObjects.find(cname) == primitiveObjects.end()) { return; } - primitiveObjects[cname]->InstallPrimitives(clazz, classSide); + primitiveObjects[cname]->InstallPrimitives(clazz, classSide, showWarning); } diff --git a/src/primitivesCore/PrimitiveLoader.h b/src/primitivesCore/PrimitiveLoader.h index 46150751..9411d333 100644 --- a/src/primitivesCore/PrimitiveLoader.h +++ b/src/primitivesCore/PrimitiveLoader.h @@ -51,12 +51,17 @@ class PrimitiveLoader { static void InstallPrimitives(const std::string& cname, VMClass* clazz, - bool classSide); + bool classSide, + bool showWarning); + + static PrimitiveLoader* GetInstance(); + PrimitiveContainer* GetObject(const std::string& name); private: void installPrimitives(const std::string& cname, VMClass* clazz, - bool classSide); + bool classSide, + bool showWarning); bool supportsClass(const std::string& name); diff --git a/src/primitivesCore/Primitives.h b/src/primitivesCore/Primitives.h index 5e3f1b13..1bb20292 100644 --- a/src/primitivesCore/Primitives.h +++ b/src/primitivesCore/Primitives.h @@ -9,15 +9,18 @@ using TernaryPrimitiveRoutine = vm_oop_t(vm_oop_t, vm_oop_t, vm_oop_t); class Prim { public: - explicit Prim(bool classSide) : isClassSide(classSide) {} + explicit Prim(bool classSide, size_t bytecodeHash) + : isClassSide(classSide), bytecodeHash(bytecodeHash) {} + bool isClassSide; + size_t bytecodeHash; }; class FramePrim : public Prim { public: - FramePrim(FramePrimitiveRoutine ptr, bool classSide) - : Prim(classSide), pointer(ptr) {} - explicit FramePrim() : Prim(false), pointer(nullptr) {} + FramePrim(FramePrimitiveRoutine ptr, bool classSide, size_t hash) + : Prim(classSide, hash), pointer(ptr) {} + explicit FramePrim() : Prim(false, 0), pointer(nullptr) {} [[nodiscard]] bool IsValid() const { return pointer != nullptr; } void MarkObjectAsInvalid() { pointer = nullptr; } @@ -27,9 +30,9 @@ class FramePrim : public Prim { class UnaryPrim : public Prim { public: - UnaryPrim(UnaryPrimitiveRoutine ptr, bool classSide) - : Prim(classSide), pointer(ptr) {} - explicit UnaryPrim() : Prim(false), pointer(nullptr) {} + UnaryPrim(UnaryPrimitiveRoutine ptr, bool classSide, size_t hash) + : Prim(classSide, hash), pointer(ptr) {} + explicit UnaryPrim() : Prim(false, 0), pointer(nullptr) {} [[nodiscard]] bool IsValid() const { return pointer != nullptr; } void MarkObjectAsInvalid() { pointer = nullptr; } @@ -39,9 +42,9 @@ class UnaryPrim : public Prim { class BinaryPrim : public Prim { public: - BinaryPrim(BinaryPrimitiveRoutine ptr, bool classSide) - : Prim(classSide), pointer(ptr) {} - explicit BinaryPrim() : Prim(false), pointer(nullptr) {} + BinaryPrim(BinaryPrimitiveRoutine ptr, bool classSide, size_t hash) + : Prim(classSide, hash), pointer(ptr) {} + explicit BinaryPrim() : Prim(false, 0), pointer(nullptr) {} [[nodiscard]] bool IsValid() const { return pointer != nullptr; } @@ -52,9 +55,9 @@ class BinaryPrim : public Prim { class TernaryPrim : public Prim { public: - TernaryPrim(TernaryPrimitiveRoutine ptr, bool classSide) - : Prim(classSide), pointer(ptr) {} - explicit TernaryPrim() : Prim(false), pointer(nullptr) {} + TernaryPrim(TernaryPrimitiveRoutine ptr, bool classSide, size_t hash) + : Prim(classSide, hash), pointer(ptr) {} + explicit TernaryPrim() : Prim(false, 0), pointer(nullptr) {} [[nodiscard]] bool IsValid() const { return pointer != nullptr; } diff --git a/src/unitTests/CloneObjectsTest.cpp b/src/unitTests/CloneObjectsTest.cpp index 79b2678c..c1bb915a 100644 --- a/src/unitTests/CloneObjectsTest.cpp +++ b/src/unitTests/CloneObjectsTest.cpp @@ -169,7 +169,8 @@ void CloneObjectsTest::testCloneBlock() { } void CloneObjectsTest::testClonePrimitive() { VMSymbol* primitiveSymbol = NewSymbol("myPrimitive"); - VMPrimitive* orig = VMPrimitive::GetEmptyPrimitive(primitiveSymbol, false); + auto* orig = dynamic_cast( + VMPrimitive::GetEmptyPrimitive(primitiveSymbol, false)); VMPrimitive* clone = orig->CloneForMovingGC(); CPPUNIT_ASSERT_EQUAL_MESSAGE("signature differs!!", orig->signature, clone->signature); diff --git a/src/unitTests/HashingTest.cpp b/src/unitTests/HashingTest.cpp new file mode 100644 index 00000000..a829f6f9 --- /dev/null +++ b/src/unitTests/HashingTest.cpp @@ -0,0 +1,47 @@ +#include "HashingTest.h" + +#include +#include +#include + +#include "../misc/Murmur3Hash.h" + +void HashingTest::testMurmur3HashWithSeeds() { + // Hex keys + const char* input1 = ""; + + // NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) + const uint8_t* in1 = reinterpret_cast(input1); + + CPPUNIT_ASSERT_EQUAL(0x00000000U, + murmur3_32(in1, strlen(input1), 0x00000000)); + CPPUNIT_ASSERT_EQUAL(0x514e28b7U, + murmur3_32(in1, strlen(input1), 0x00000001)); + CPPUNIT_ASSERT_EQUAL(0x81f16f39U, + murmur3_32(in1, strlen(input1), 0xffffffff)); + + // Strings + const char* str1 = "test"; + + // NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) + const uint8_t* s1 = reinterpret_cast(str1); + + CPPUNIT_ASSERT_EQUAL(0xba6bd213U, murmur3_32(s1, strlen(str1), 0x00000000)); + CPPUNIT_ASSERT_EQUAL(0x704b81dcU, murmur3_32(s1, strlen(str1), 0x9747b28c)); + + const char* str2 = "Hello, world!"; + + // NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) + const uint8_t* s2 = reinterpret_cast(str2); + + CPPUNIT_ASSERT_EQUAL(0xc0363e43U, murmur3_32(s2, strlen(str2), 0x00000000)); + CPPUNIT_ASSERT_EQUAL(0x24884cbaU, murmur3_32(s2, strlen(str2), 0x9747b28c)); + + const char* str3 = "The quick brown fox jumps over the lazy dog"; + + // NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) + const uint8_t* s3 = reinterpret_cast(str3); + + CPPUNIT_ASSERT_EQUAL(0x2e4ff723U, murmur3_32(s3, strlen(str3), 0x00000000)); + CPPUNIT_ASSERT_EQUAL(0x2fa826cdU, murmur3_32(s3, strlen(str3), 0x9747b28c)); +} diff --git a/src/unitTests/HashingTest.h b/src/unitTests/HashingTest.h new file mode 100644 index 00000000..dae912f5 --- /dev/null +++ b/src/unitTests/HashingTest.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "TestWithParsing.h" + +using namespace std; + +class HashingTest : public CPPUNIT_NS::TestCase { + CPPUNIT_TEST_SUITE(HashingTest); // NOLINT(misc-const-correctness) + CPPUNIT_TEST(testMurmur3HashWithSeeds); + CPPUNIT_TEST_SUITE_END(); + +private: + static void testMurmur3HashWithSeeds(); +}; diff --git a/src/unitTests/WalkObjectsTest.cpp b/src/unitTests/WalkObjectsTest.cpp index 3d733bb8..68912356 100644 --- a/src/unitTests/WalkObjectsTest.cpp +++ b/src/unitTests/WalkObjectsTest.cpp @@ -135,7 +135,7 @@ void WalkObjectsTest::testWalkClass() { void WalkObjectsTest::testWalkPrimitive() { walkedObjects.clear(); VMSymbol* primitiveSymbol = NewSymbol("myPrimitive"); - VMPrimitive* prim = VMPrimitive::GetEmptyPrimitive(primitiveSymbol, false); + VMInvokable* prim = VMPrimitive::GetEmptyPrimitive(primitiveSymbol, false); prim->SetHolder(load_ptr(methodClass)); prim->WalkObjects(collectMembers); diff --git a/src/unitTests/main.cpp b/src/unitTests/main.cpp index 6b9a90b8..e891e007 100644 --- a/src/unitTests/main.cpp +++ b/src/unitTests/main.cpp @@ -21,6 +21,7 @@ #include "BasicInterpreterTests.h" #include "BytecodeGenerationTest.h" #include "CloneObjectsTest.h" +#include "HashingTest.h" #include "TrivialMethodTest.h" #include "WalkObjectsTest.h" @@ -36,6 +37,7 @@ CPPUNIT_TEST_SUITE_REGISTRATION(WriteBarrierTest); CPPUNIT_TEST_SUITE_REGISTRATION(BytecodeGenerationTest); CPPUNIT_TEST_SUITE_REGISTRATION(TrivialMethodTest); CPPUNIT_TEST_SUITE_REGISTRATION(BasicInterpreterTests); +CPPUNIT_TEST_SUITE_REGISTRATION(HashingTest); int32_t main(int32_t ac, char** av) { Universe::Start(ac, av); diff --git a/src/vm/Globals.cpp b/src/vm/Globals.cpp index c95d46f2..f4071c06 100644 --- a/src/vm/Globals.cpp +++ b/src/vm/Globals.cpp @@ -13,6 +13,7 @@ GCClass* metaClassClass; GCClass* nilClass; GCClass* integerClass; GCClass* arrayClass; +GCClass* vectorClass; GCClass* methodClass; GCClass* symbolClass; GCClass* primitiveClass; diff --git a/src/vm/Globals.h b/src/vm/Globals.h index 12fefc60..e63c7b4f 100644 --- a/src/vm/Globals.h +++ b/src/vm/Globals.h @@ -13,6 +13,7 @@ extern GCClass* metaClassClass; extern GCClass* nilClass; extern GCClass* integerClass; extern GCClass* arrayClass; +extern GCClass* vectorClass; extern GCClass* methodClass; extern GCClass* symbolClass; extern GCClass* primitiveClass; diff --git a/src/vm/IsValidObject.cpp b/src/vm/IsValidObject.cpp index bcf1885b..1f570a41 100644 --- a/src/vm/IsValidObject.cpp +++ b/src/vm/IsValidObject.cpp @@ -21,9 +21,11 @@ #include "../vmobjects/VMString.h" #include "../vmobjects/VMSymbol.h" #include "../vmobjects/VMTrivialMethod.h" +#include "../vmobjects/VMVector.h" #include "Globals.h" static void* vt_array; +static void* vt_vector; static void* vt_block; static void* vt_class; static void* vt_double; @@ -73,7 +75,8 @@ bool IsValidObject(vm_oop_t obj) { vt == vt_primitive || vt == vt_safe_un_primitive || vt == vt_safe_bin_primitive || vt == vt_safe_ter_primitive || vt == vt_string || vt == vt_symbol || vt == vt_literal_return || - vt == vt_global_return || vt == vt_getter || vt == vt_setter; + vt == vt_global_return || vt == vt_getter || vt == vt_setter || + vt == vt_vector; if (!b) { assert(b && "Expected vtable to be one of the known ones."); return false; @@ -100,6 +103,7 @@ bool IsValidObject(vm_oop_t obj) { void set_vt_to_null() { vt_array = nullptr; + vt_vector = nullptr; vt_block = nullptr; vt_class = nullptr; vt_double = nullptr; @@ -170,6 +174,10 @@ void obtain_vtables_of_known_classes(VMSymbol* someValidSymbol) { auto* arr = new (GetHeap(), 0) VMArray(0, 0); vt_array = get_vtable(arr); + auto* vec = + new (GetHeap(), 0) VMVector(nullptr, nullptr, nullptr); + vt_vector = get_vtable(vec); + auto* blck = new (GetHeap(), 0) VMBlock(nullptr, nullptr); vt_block = get_vtable(blck); diff --git a/src/vm/Universe.cpp b/src/vm/Universe.cpp index 017a5539..9d3af3b9 100644 --- a/src/vm/Universe.cpp +++ b/src/vm/Universe.cpp @@ -56,6 +56,7 @@ #include "../vmobjects/VMObject.h" #include "../vmobjects/VMObjectBase.h" #include "../vmobjects/VMString.h" +#include "../vmobjects/VMVector.h" #include "Globals.h" #include "IsValidObject.h" #include "LogAllocation.h" @@ -73,6 +74,7 @@ static gc_oop_t prebuildInts[INT_CACHE_MAX_VALUE - INT_CACHE_MIN_VALUE + 1]; uint8_t dumpBytecodes; uint8_t gcVerbosity; +bool abortOnCoreLibHashMismatch = false; static std::string bm_name; @@ -167,6 +169,12 @@ static void printVmConfig() { cout << "\tnot caching integers\n"; } + if (USE_VECTOR_PRIMITIVES) { + cout << "\tVector primitives: enabled\n"; + } else { + cout << "\tVector primitives: disabled\n"; + } + cout << "--------------------------------------\n"; } @@ -204,6 +212,8 @@ vector Universe::handleArguments(int32_t argc, char** argv) { } else if ((strncmp(argv[i], "-h", 2) == 0) || (strncmp(argv[i], "--help", 6) == 0)) { printUsageAndExit(argv[0]); + } else if ((strncmp(argv[i], "-prim-hash-check", 16)) == 0) { + abortOnCoreLibHashMismatch = true; } else { vector extPathTokens = vector(2); std::string const tmpString = std::string(argv[i]); @@ -273,6 +283,10 @@ void Universe::printUsageAndExit(char* executable) { cout << " set search path for application classes\n"; cout << " -d enable disassembling (twice for tracing)\n"; cout << " -cfg print VM configuration\n"; + cout << " -prim-hash-check check that method replacements have expected " + "hash.\n"; + cout + << " Exit with error when a hash does not match.\n"; cout << " -g enable garbage collection details:\n" << " 1x - print statistics when VM shuts down\n" @@ -281,7 +295,7 @@ void Universe::printUsageAndExit(char* executable) { << "\n"; cout << " -HxMB set the heap size to x MB (default: 1 MB)\n"; cout << " -HxKB set the heap size to x KB (default: 1 MB)\n"; - cout << " -h show this help\n"; + cout << " -h|--help show this help\n"; Quit(ERR_SUCCESS); } @@ -418,6 +432,7 @@ VMObject* Universe::InitializeGlobals() { nilClass = store_root(NewSystemClass()); classClass = store_root(NewSystemClass()); arrayClass = store_root(NewSystemClass()); + vectorClass = store_root(NewSystemClass()); symbolClass = store_root(NewSystemClass()); methodClass = store_root(NewSystemClass()); integerClass = store_root(NewSystemClass()); @@ -433,6 +448,8 @@ VMObject* Universe::InitializeGlobals() { "Metaclass"); InitializeSystemClass(load_ptr(nilClass), load_ptr(objectClass), "Nil"); InitializeSystemClass(load_ptr(arrayClass), load_ptr(objectClass), "Array"); + InitializeSystemClass(load_ptr(vectorClass), load_ptr(objectClass), + "Vector"); InitializeSystemClass(load_ptr(methodClass), load_ptr(arrayClass), "Method"); InitializeSystemClass(load_ptr(stringClass), load_ptr(objectClass), @@ -460,6 +477,7 @@ VMObject* Universe::InitializeGlobals() { LoadSystemClass(load_ptr(metaClassClass)); LoadSystemClass(load_ptr(nilClass)); LoadSystemClass(load_ptr(arrayClass)); + LoadSystemClass(load_ptr(vectorClass)); LoadSystemClass(load_ptr(methodClass)); LoadSystemClass(load_ptr(symbolClass)); LoadSystemClass(load_ptr(integerClass)); @@ -514,8 +532,9 @@ VMClass* Universe::GetBlockClassWithArgs(uint8_t numberOfArguments) { VMSymbol* name = SymbolFor(Str.str()); VMClass* result = LoadClassBasic(name, nullptr); - result->AddInstanceInvokable(new (GetHeap(), 0) - VMEvaluationPrimitive(numberOfArguments)); + result->InstallPrimitive(new (GetHeap(), 0) + VMEvaluationPrimitive(numberOfArguments), + 0, false); SetGlobal(name, result); blockClassesByNoOfArgs[numberOfArguments] = store_root(result); @@ -582,7 +601,7 @@ VMClass* Universe::LoadClass(VMSymbol* name) { } if (result->HasPrimitives() || result->GetClass()->HasPrimitives()) { - result->LoadPrimitives(); + result->LoadPrimitives(true); } SetGlobal(name, result); @@ -601,6 +620,7 @@ VMClass* Universe::LoadClassBasic(VMSymbol* name, VMClass* systemClass) { Disassembler::Dump(result->GetClass()); Disassembler::Dump(result); } + return result; } } @@ -624,11 +644,26 @@ void Universe::LoadSystemClass(VMClass* systemClass) { Quit(ERR_FAIL); } - if (result->HasPrimitives() || result->GetClass()->HasPrimitives()) { - result->LoadPrimitives(); + // Vector has primitive methods and should be loaded (This is temporary) + if (result->HasPrimitives() || result->GetClass()->HasPrimitives() || + (result->GetName()->GetStdString() == "Vector" && + USE_VECTOR_PRIMITIVES == true)) { + result->LoadPrimitives(false); } } +// Should create a new instance of Vector +VMVector* Universe::NewVector(size_t size, VMClass* cls) { + vm_oop_t first = NEW_INT(1); + vm_oop_t last = NEW_INT(1); + auto* storageArray = NewArray(size); + auto* result = + new (GetHeap(), 0) VMVector(first, last, storageArray); + result->SetClass(cls); + LOG_ALLOCATION("VMVector", result->GetObjectSize()); + return result; +} + VMArray* Universe::NewArray(size_t size) { size_t const additionalBytes = size * sizeof(VMObject*); @@ -654,6 +689,38 @@ VMArray* Universe::NewArray(size_t size) { return result; } +VMArray* Universe::NewExpandedArrayFromArray(size_t size, VMArray* array) { + size_t const additionalBytes = size * sizeof(VMObject*); + + bool outsideNursery = false; // NOLINT + + size_t const currentArraySize = array->GetNumberOfIndexableFields(); + +#if GC_TYPE == GENERATIONAL + // if the array is too big for the nursery, we will directly allocate a + // mature object + outsideNursery = additionalBytes + sizeof(VMArray) > + GetHeap()->GetMaxNurseryObjectSize(); +#endif + + auto* result = new (GetHeap(), + additionalBytes ALLOC_OUTSIDE_NURSERY(outsideNursery)) + VMArray(size, additionalBytes, currentArraySize); + if ((GC_TYPE == GENERATIONAL) && outsideNursery) { + result->SetGCField(MASK_OBJECT_IS_OLD); + } + + result->SetClass(load_ptr(arrayClass)); + + LOG_ALLOCATION("VMArray", result->GetObjectSize()); + + // Now copy the contents of the old array into the new one + for (size_t i = 0; i < currentArraySize; ++i) { + result->SetIndexableField(i, array->GetIndexableField(i)); + } + return result; +} + VMArray* Universe::NewArrayFromStrings(const vector& strings) { VMArray* result = NewArray(strings.size()); size_t j = 0; @@ -812,6 +879,7 @@ void Universe::WalkGlobals(walk_heap_fn walk) { nilClass = static_cast(walk(nilClass)); integerClass = static_cast(walk(integerClass)); arrayClass = static_cast(walk(arrayClass)); + vectorClass = static_cast(walk(vectorClass)); methodClass = static_cast(walk(methodClass)); symbolClass = static_cast(walk(symbolClass)); primitiveClass = static_cast(walk(primitiveClass)); diff --git a/src/vm/Universe.h b/src/vm/Universe.h index 9b289f91..f566412f 100644 --- a/src/vm/Universe.h +++ b/src/vm/Universe.h @@ -41,6 +41,7 @@ class SourcecodeCompiler; // for runtime debug extern uint8_t dumpBytecodes; extern uint8_t gcVerbosity; +extern bool abortOnCoreLibHashMismatch; using namespace std; class Universe { @@ -59,6 +60,8 @@ class Universe { // VMObject instanciation methods. These should probably be refactored to a // new class static VMArray* NewArray(size_t /*size*/); + static VMArray* NewExpandedArrayFromArray(size_t size, VMArray* array); + static VMVector* NewVector(size_t /*size*/, VMClass* cls); static VMArray* NewArrayList(std::vector& list); static VMArray* NewArrayList(std::vector& list); diff --git a/src/vmobjects/ObjectFormats.h b/src/vmobjects/ObjectFormats.h index 9038985b..415afbc5 100644 --- a/src/vmobjects/ObjectFormats.h +++ b/src/vmobjects/ObjectFormats.h @@ -76,6 +76,7 @@ // Forward definitions of VM object classes class AbstractVMObject; class VMArray; +class VMVector; class VMBlock; class VMClass; class VMDouble; @@ -144,6 +145,7 @@ class GCObject : public GCAbstractObject { public: typedef VMObject class GCFrame : public GCAbstractObject { public: typedef VMFrame Loaded; }; class GCClass : public GCObject { public: typedef VMClass Loaded; }; class GCArray : public GCObject { public: typedef VMArray Loaded; }; +class GCVector : public GCObject { public: typedef VMVector Loaded; }; class GCBlock : public GCObject { public: typedef VMBlock Loaded; }; class GCDouble : public GCAbstractObject { public: typedef VMDouble Loaded; }; class GCInteger : public GCAbstractObject { public: typedef VMInteger Loaded; }; diff --git a/src/vmobjects/VMArray.h b/src/vmobjects/VMArray.h index ea1894b3..8358e6ed 100644 --- a/src/vmobjects/VMArray.h +++ b/src/vmobjects/VMArray.h @@ -46,6 +46,16 @@ class VMArray : public VMObject { assert(VMArrayNumberOfFields == 0); } + explicit VMArray(size_t arraySize, + size_t additionalBytes, + size_t nillableFrom) + : VMObject(arraySize + + 0 /* VMArray is not allowed to have any fields itself */, + additionalBytes + sizeof(VMArray), + nillableFrom) { + assert(VMArrayNumberOfFields == 0); + } + // VMArray doesn't need to customize `void WalkObjects(walk_heap_fn)`, // because it doesn't need anything special. diff --git a/src/vmobjects/VMClass.cpp b/src/vmobjects/VMClass.cpp index abbf721c..955e9a5b 100644 --- a/src/vmobjects/VMClass.cpp +++ b/src/vmobjects/VMClass.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "../memory/Heap.h" @@ -40,6 +41,7 @@ #include "ObjectFormats.h" #include "VMArray.h" #include "VMInvokable.h" +#include "VMMethod.h" #include "VMObject.h" #include "VMSymbol.h" @@ -63,36 +65,79 @@ VMClass::VMClass(size_t numberOfFields, size_t additionalBytes) : VMObject(numberOfFields + VMClassNumberOfFields, additionalBytes + sizeof(VMClass)) {} -bool VMClass::AddInstanceInvokable(VMInvokable* invokable) { +PrimInstallResult VMClass::InstallPrimitive(VMInvokable* invokable, + size_t bytecodeHash, + bool reportHashMismatch) { if (invokable == nullptr) { ErrorExit("Error: trying to add non-invokable to invokables array"); - return false; + return PrimInstallResult::NULL_ARG; } + // Check whether an invokable with the same signature exists and replace it // if that's the case VMArray* instInvokables = load_ptr(instanceInvokables); size_t const numIndexableFields = instInvokables->GetNumberOfIndexableFields(); + for (size_t i = 0; i < numIndexableFields; ++i) { auto* inv = static_cast(instInvokables->GetIndexableField(i)); - if (inv != nullptr) { - if (invokable->GetSignature() == inv->GetSignature()) { - SetInstanceInvokable(i, invokable); - return false; - } - } else { + + if (inv == nullptr) { ErrorExit( "Invokables array corrupted. " "Either NULL pointer added or pointer to non-invokable."); - return false; + return PrimInstallResult::NULL_IN_INVOKABLES; + } + + if (invokable->GetSignature() == inv->GetSignature()) { + PrimInstallResult result = PrimInstallResult::NULL_ARG; + bool hashMismatch = false; + size_t seenHash = 0; + + if (bytecodeHash != 0) { + auto* method = dynamic_cast(inv); + if (method == nullptr) { + assert(inv->GetHolder() != nullptr); + ErrorPrint( + "Expected a bytecode method, but found something else " + "for " + + inv->GetHolder()->GetName()->GetStdString() + ">>#" + + inv->GetSignature()->GetStdString()); + result = PrimInstallResult::HASH_MISMATCH; + hashMismatch = true; + } else { + seenHash = method->GetBytecodeHash(); + if (seenHash == bytecodeHash) { + result = PrimInstallResult::INSTALLED_REPLACED; + } else { + result = PrimInstallResult::HASH_MISMATCH; + hashMismatch = true; + } + } + } else { + result = PrimInstallResult::INSTALLED_REPLACED; + } + + if (!hashMismatch) { + SetInstanceInvokable(i, invokable); + } else if (reportHashMismatch) { + cout << "Warn: Primitive " + << inv->GetHolder()->GetName()->GetStdString() << ">>#" + << invokable->GetSignature()->GetStdString() + << " was not installed.\n" + << "Warn: Bytecode hash did not match.\n" + << "Warn: expected hash: " << bytecodeHash << "\n" + << "Warn: actual hash: " << seenHash << "\n"; + } + return result; } } // it's a new invokable so we need to expand the invokables array. store_ptr(instanceInvokables, instInvokables->CopyAndExtendWith((vm_oop_t)invokable)); - return true; + return PrimInstallResult::INSTALLED_ADDED; } VMSymbol* VMClass::GetInstanceFieldName(size_t index) const { @@ -193,12 +238,13 @@ bool VMClass::HasPrimitives() const { return false; } -void VMClass::LoadPrimitives() { +void VMClass::LoadPrimitives(bool showWarning) { std::string const cname = load_ptr(name)->GetStdString(); if (hasPrimitivesFor(cname)) { - PrimitiveLoader::InstallPrimitives(cname, this, false); - PrimitiveLoader::InstallPrimitives(cname, GetClass(), true); + PrimitiveLoader::InstallPrimitives(cname, this, false, showWarning); + PrimitiveLoader::InstallPrimitives( + cname, GetClass(), true, showWarning); } } diff --git a/src/vmobjects/VMClass.h b/src/vmobjects/VMClass.h index 007fd627..d9833f72 100644 --- a/src/vmobjects/VMClass.h +++ b/src/vmobjects/VMClass.h @@ -41,6 +41,14 @@ class ClassGenerationContext; +enum PrimInstallResult : uint8_t { + NULL_ARG, + NULL_IN_INVOKABLES, + HASH_MISMATCH, + INSTALLED_REPLACED, + INSTALLED_ADDED +}; + class VMClass : public VMObject { public: typedef GCClass Stored; @@ -62,11 +70,13 @@ class VMClass : public VMObject { void SetInstanceInvokable(size_t index, VMInvokable* invokable); VMInvokable* LookupInvokable(VMSymbol* name); int64_t LookupFieldIndex(VMSymbol* name) const; - bool AddInstanceInvokable(VMInvokable* invokable); + PrimInstallResult InstallPrimitive(VMInvokable* invokable, + size_t bytecodeHash, + bool reportHashMismatch); [[nodiscard]] VMSymbol* GetInstanceFieldName(size_t index) const; [[nodiscard]] size_t GetNumberOfInstanceFields() const; [[nodiscard]] bool HasPrimitives() const; - void LoadPrimitives(); + void LoadPrimitives(bool showWarning); [[nodiscard]] VMClass* CloneForMovingGC() const override; [[nodiscard]] std::string AsDebugString() const override; diff --git a/src/vmobjects/VMMethod.cpp b/src/vmobjects/VMMethod.cpp index e5d4c496..17d04a9f 100644 --- a/src/vmobjects/VMMethod.cpp +++ b/src/vmobjects/VMMethod.cpp @@ -41,6 +41,7 @@ #include "../interpreter/Interpreter.h" #include "../interpreter/bytecodes.h" #include "../memory/Heap.h" +#include "../misc/Murmur3Hash.h" #include "../misc/defs.h" #include "../vm/Globals.h" #include "../vm/Print.h" @@ -710,3 +711,7 @@ void VMMethod::MergeScopeInto(MethodGenerationContext& mgenc) { assert(lexicalScope != nullptr); mgenc.MergeIntoScope(*lexicalScope); } + +size_t VMMethod::GetBytecodeHash() const { + return murmur3_32(bytecodes, bcLength, 0x00000000); +} diff --git a/src/vmobjects/VMMethod.h b/src/vmobjects/VMMethod.h index e25da6ab..7a20da0f 100644 --- a/src/vmobjects/VMMethod.h +++ b/src/vmobjects/VMMethod.h @@ -121,6 +121,9 @@ class VMMethod : public VMInvokable { } [[nodiscard]] size_t GetNumberOfBytecodes() const { return bcLength; } + + [[nodiscard]] size_t GetBytecodeHash() const; + void SetHolder(VMClass* hld) override; void SetHolderAll(VMClass* hld) const; @@ -184,6 +187,8 @@ class VMMethod : public VMInvokable { void Dump(const char* indent, bool printObjects) override; + [[nodiscard]] inline uint8_t* GetBytecodes() const { return bytecodes; } + private: void inlineInto(MethodGenerationContext& mgenc, const Parser& parser); std::priority_queue createBackJumpHeap(); @@ -199,8 +204,6 @@ class VMMethod : public VMInvokable { make_testable(public); - [[nodiscard]] inline uint8_t* GetBytecodes() const { return bytecodes; } - [[nodiscard]] inline vm_oop_t GetIndexableField(size_t idx) const { return load_ptr(indexableFields[idx]); } diff --git a/src/vmobjects/VMObject.cpp b/src/vmobjects/VMObject.cpp index d2fbf298..d29f6e73 100644 --- a/src/vmobjects/VMObject.cpp +++ b/src/vmobjects/VMObject.cpp @@ -59,6 +59,13 @@ void VMObject::nilInitializeFields() { } } +/* Allows only a certain subset of fields to be made nil */ +void VMObject::nilInitializeFieldsFrom(size_t nillableFrom) { + for (size_t i = nillableFrom; i < numberOfFields; ++i) { + FIELDS[i] = nilObject; + } +} + void VMObject::SetClass(VMClass* cl) { store_ptr(clazz, cl); } diff --git a/src/vmobjects/VMObject.h b/src/vmobjects/VMObject.h index 842ce6c3..ea302d9e 100644 --- a/src/vmobjects/VMObject.h +++ b/src/vmobjects/VMObject.h @@ -77,6 +77,22 @@ class VMObject : public AbstractVMObject { nilInitializeFields(); } + /* Constructor to be used when making fields nil is only required from a + * certain index onwards */ + explicit VMObject(size_t numSubclassFields, size_t totalObjectSize, + size_t nillableFrom) + : totalObjectSize(totalObjectSize), + numberOfFields(VMObjectNumberOfFields + numSubclassFields) { + assert(IS_PADDED_SIZE(totalObjectSize)); + assert(totalObjectSize >= sizeof(VMObject)); + + // this line would be needed if the VMObject** is used instead of the + // macro: FIELDS = (VMObject**)&clazz; + hash = (intptr_t)this; + + nilInitializeFieldsFrom(nillableFrom); + } + ~VMObject() override = default; [[nodiscard]] int64_t GetHash() const override { return hash; } @@ -121,6 +137,7 @@ class VMObject : public AbstractVMObject { protected: void nilInitializeFields(); + void nilInitializeFieldsFrom(size_t nillableFrom); // VMObject essentials int64_t hash; diff --git a/src/vmobjects/VMPrimitive.cpp b/src/vmobjects/VMPrimitive.cpp index 36b82e98..4adf1392 100644 --- a/src/vmobjects/VMPrimitive.cpp +++ b/src/vmobjects/VMPrimitive.cpp @@ -40,7 +40,7 @@ #include "VMMethod.h" #include "VMSymbol.h" -VMPrimitive* VMPrimitive::GetFramePrim(VMSymbol* sig, FramePrim prim) { +VMInvokable* VMPrimitive::GetFramePrim(VMSymbol* sig, FramePrim prim) { auto* p = new (GetHeap(), 0) VMPrimitive(sig, prim); return p; } @@ -56,8 +56,8 @@ static void emptyRoutine(VMFrame* frame) { ErrorExit("undefined primitive called"); } -VMPrimitive* VMPrimitive::GetEmptyPrimitive(VMSymbol* sig, bool classSide) { - return GetFramePrim(sig, FramePrim(&emptyRoutine, classSide)); +VMInvokable* VMPrimitive::GetEmptyPrimitive(VMSymbol* sig, bool classSide) { + return GetFramePrim(sig, FramePrim(&emptyRoutine, classSide, 0)); } bool VMPrimitive::IsEmpty() const { diff --git a/src/vmobjects/VMPrimitive.h b/src/vmobjects/VMPrimitive.h index b37c4dbb..a12c600d 100644 --- a/src/vmobjects/VMPrimitive.h +++ b/src/vmobjects/VMPrimitive.h @@ -35,8 +35,8 @@ class VMPrimitive : public VMInvokable { public: typedef GCPrimitive Stored; - static VMPrimitive* GetEmptyPrimitive(VMSymbol* sig, bool classSide); - static VMPrimitive* GetFramePrim(VMSymbol* sig, FramePrim prim); + static VMInvokable* GetEmptyPrimitive(VMSymbol* sig, bool classSide); + static VMInvokable* GetFramePrim(VMSymbol* sig, FramePrim prim); VMPrimitive(VMSymbol* sig, FramePrim prim) : VMInvokable(sig), prim(prim) { write_barrier(this, sig); diff --git a/src/vmobjects/VMSafePrimitive.cpp b/src/vmobjects/VMSafePrimitive.cpp index 1cac43bc..4d0d33e5 100644 --- a/src/vmobjects/VMSafePrimitive.cpp +++ b/src/vmobjects/VMSafePrimitive.cpp @@ -14,7 +14,7 @@ #include "VMMethod.h" #include "VMSymbol.h" -VMSafePrimitive* VMSafePrimitive::GetSafeUnary(VMSymbol* sig, UnaryPrim prim) { +VMInvokable* VMSafePrimitive::GetSafeUnary(VMSymbol* sig, UnaryPrim prim) { auto* p = new (GetHeap(), 0) VMSafeUnaryPrimitive(sig, prim); return p; } @@ -33,8 +33,7 @@ VMFrame* VMSafeUnaryPrimitive::Invoke1(VMFrame* frame) { return nullptr; } -VMSafePrimitive* VMSafePrimitive::GetSafeBinary(VMSymbol* sig, - BinaryPrim prim) { +VMInvokable* VMSafePrimitive::GetSafeBinary(VMSymbol* sig, BinaryPrim prim) { auto* p = new (GetHeap(), 0) VMSafeBinaryPrimitive(sig, prim); return p; } @@ -51,8 +50,7 @@ VMFrame* VMSafeBinaryPrimitive::Invoke1(VMFrame* /*frame*/) { ErrorExit("Unary invoke on binary primitive"); } -VMSafePrimitive* VMSafePrimitive::GetSafeTernary(VMSymbol* sig, - TernaryPrim prim) { +VMInvokable* VMSafePrimitive::GetSafeTernary(VMSymbol* sig, TernaryPrim prim) { auto* p = new (GetHeap(), 0) VMSafeTernaryPrimitive(sig, prim); return p; } diff --git a/src/vmobjects/VMSafePrimitive.h b/src/vmobjects/VMSafePrimitive.h index bbad44e9..8327008d 100644 --- a/src/vmobjects/VMSafePrimitive.h +++ b/src/vmobjects/VMSafePrimitive.h @@ -19,9 +19,9 @@ class VMSafePrimitive : public VMInvokable { void InlineInto(MethodGenerationContext& mgenc, const Parser& parser, bool mergeScope = true) final; - static VMSafePrimitive* GetSafeUnary(VMSymbol* sig, UnaryPrim prim); - static VMSafePrimitive* GetSafeBinary(VMSymbol* sig, BinaryPrim prim); - static VMSafePrimitive* GetSafeTernary(VMSymbol* sig, TernaryPrim prim); + static VMInvokable* GetSafeUnary(VMSymbol* sig, UnaryPrim prim); + static VMInvokable* GetSafeBinary(VMSymbol* sig, BinaryPrim prim); + static VMInvokable* GetSafeTernary(VMSymbol* sig, TernaryPrim prim); [[nodiscard]] std::string AsDebugString() const final; diff --git a/src/vmobjects/VMVector.cpp b/src/vmobjects/VMVector.cpp new file mode 100644 index 00000000..1aacc70a --- /dev/null +++ b/src/vmobjects/VMVector.cpp @@ -0,0 +1,231 @@ +#include "../vmobjects/VMVector.h" + +#include +#include +#include +#include +#include + +#include "../interpreter/Interpreter.h" +#include "../misc/defs.h" +#include "../vm/Globals.h" +#include "../vm/Universe.h" +#include "../vmobjects/ObjectFormats.h" + +const size_t VMVector::VMVectorNumberOfFields = 3; + +VMVector::VMVector(vm_oop_t first, vm_oop_t last, VMArray* storage) + : VMObject(VMVectorNumberOfFields, sizeof(VMVector)), + first(store_with_separate_barrier(first)), + last(store_with_separate_barrier(last)), + storage(store_with_separate_barrier(storage)) { + static_assert(VMVectorNumberOfFields == 3); + write_barrier(this, storage); +} + +vm_oop_t VMVector::AWFYGetStorage(int64_t index) { + // This is a method that is used by the AWFY tests, it does not handle + // 1-0 indexing conversion + int64_t const first = INT_VAL(load_ptr(this->first)); + VMArray* const storage = load_ptr(this->storage); + + if (index > storage->GetNumberOfIndexableFields()) { + return load_ptr(nilObject); // AWFY does not handle an IndexOutOfBounds + // in this case + } + vm_oop_t returned = storage->GetIndexableField( + (first - 1) + (index - 1)); // Convert to 0-indexing + return returned; +} + +vm_oop_t VMVector::GetStorage(int64_t index) { + int64_t const first = INT_VAL(load_ptr(this->first)); + int64_t const last = INT_VAL(load_ptr(this->last)); + VMArray* const storage = load_ptr(this->storage); + + if (index < 1 || index > last - first) { + return IndexOutOfBounds(first + last - 1, index); + } + vm_oop_t returned = storage->GetIndexableField( + (first - 1) + (index - 1)); // Convert to 0-indexing + return returned; +} + +/* Returns the value currently held at that location */ +vm_oop_t VMVector::SetStorage(int64_t index, vm_oop_t value) { + int64_t const first = INT_VAL(load_ptr(this->first)); + int64_t const last = INT_VAL(load_ptr(this->last)); + VMArray* const storage = load_ptr(this->storage); + if (index < 1 || index > first + last) { + return IndexOutOfBounds(first + last - 1, index); + } + vm_oop_t curVal = storage->GetIndexableField(first + index - 2); + storage->SetIndexableField(first + index - 2, value); + return curVal; +} + +/* AWFY Vector can expand on at:put: core-lib vector cannot*/ +void VMVector::SetStorageAWFY(int64_t index, vm_oop_t value) { + int64_t const first = INT_VAL(load_ptr(this->first)); + int64_t last = INT_VAL(load_ptr(this->last)); + VMArray* storage = load_ptr(this->storage); + + if (index > storage->GetNumberOfIndexableFields()) { + // Expand the array + VMArray* newStorage = Universe::NewExpandedArrayFromArray( + storage->GetNumberOfIndexableFields() * 2, storage); + + newStorage->SetIndexableField(index - 1, value); + + this->storage = store_ptr(this->storage, newStorage); + + } else { + // Just set the new value + storage->SetIndexableField(first + index - 2, value); + last += 1; + this->last = store_ptr(this->last, NEW_INT(last)); + } +} + +void VMVector::Append(vm_oop_t value) { + int64_t last = INT_VAL(load_ptr(this->last)); + VMArray* storage = load_ptr(this->storage); + + if (last >= storage->GetNumberOfIndexableFields()) { // Expand the array + // Expand by the correct amount *2 by default in the native som + // implementation + VMArray* newStorage = Universe::NewExpandedArrayFromArray( + storage->GetNumberOfIndexableFields() * 2, storage); + + storage->CopyIndexableFieldsTo(newStorage); + newStorage->SetIndexableField(last - 1, value); + + SetField(2 /* storage */, newStorage); + + } else { // Just set the new value + storage->SetIndexableField(last - 1, value); + } + + last += 1; + this->last = store_ptr(this->last, NEW_INT(last)); +} + +vm_oop_t VMVector::RemoveLast() { + const int64_t last = INT_VAL(load_ptr(this->last)); + const int64_t first = INT_VAL(load_ptr(this->first)); + + // Throw an error correctly (error: method in som) + if (last == first) { + // VMSafe*Primitive::Invoke will push it right back to the same frame + VMFrame* frame = Interpreter::GetFrame(); + vm_oop_t errorMsg = + Universe::NewString("Vector: error when removing Last item."); + vm_oop_t args[1] = {errorMsg}; + this->Send("error:", args, 1); + return frame->Pop(); + } + return Remove(NEW_INT(last - first)); // Last-First gives the (User) index + // of the last element in 1-indexing +} + +vm_oop_t VMVector::RemoveFirst() { + // This method will just increment the first index + int64_t first = INT_VAL(load_ptr(this->first)); + + // Throw an error correctly (error: method in som) + if (first >= INT_VAL(load_ptr(this->last))) { + // VMSafe*Primitive::Invoke will push it right back to the same frame + VMFrame* frame = Interpreter::GetFrame(); + vm_oop_t errorMsg = + Universe::NewString("Vector: error when removing First item."); + vm_oop_t args[1] = {errorMsg}; + this->Send("error:", args, 1); + return frame->Pop(); + } + vm_oop_t itemToRemove = GetStorage( + 1); // This is 1 because GetIndexableField handles 1 to 0 indexing + first += 1; // Increment the first index + this->first = store_ptr(this->first, NEW_INT(first)); + return itemToRemove; +} + +vm_oop_t VMVector::RemoveObj(vm_oop_t other) { + const int64_t first = INT_VAL(load_ptr(this->first)); + const int64_t last = INT_VAL(load_ptr(this->last)); + VMArray* storage = load_ptr(this->storage); + + for (int64_t i = first - 1; i < last - 1; ++i) { + vm_oop_t current = storage->GetIndexableField(i); + + // Check where integers are tagged or references can be checked + if (current == other) { + Remove(NEW_INT(i - first + 2)); // Convert to 1-indexing + return load_ptr(trueObject); + } + } + return load_ptr(falseObject); +} + +vm_oop_t VMVector::Remove(vm_oop_t inx) { + const int64_t first = INT_VAL(load_ptr(this->first)); + int64_t last = INT_VAL(load_ptr(this->last)); + VMArray* storage = load_ptr(this->storage); + int64_t const index = INT_VAL(inx); + + if (index < 1 || index > last - first) { + return IndexOutOfBounds(first + last - 1, index); + } + + vm_oop_t itemToRemove = GetStorage(index); + + // Shift all elements after the index to the left + for (int64_t i = index; i < last - first; ++i) { + storage->SetIndexableField(first + i - 1, + storage->GetIndexableField(first + i)); + } + + last -= 1; + this->last = store_ptr(this->last, NEW_INT(last)); + + return itemToRemove; +} + +void VMVector::RemoveAll() { + this->first = store_ptr(this->first, NEW_INT(1)); + this->last = store_ptr(this->last, NEW_INT(1)); + VMArray* storage = load_ptr(this->storage); + VMArray* newArray = + Universe::NewArray(storage->GetNumberOfIndexableFields()); + this->storage = store_ptr(this->storage, newArray); +} + +vm_oop_t VMVector::copyStorageArray() { + const int64_t first = INT_VAL(load_ptr(this->first)); + const int64_t last = INT_VAL(load_ptr(this->last)); + VMArray* storage = load_ptr(this->storage); + + VMArray* result = Universe::NewArray(last - first); + for (int64_t i = first - 1; i < last - 1; ++i) { + result->SetIndexableField(i - first + 1, storage->GetIndexableField(i)); + } + + return result; +} + +/* Rename as a more specific error function */ +vm_oop_t VMVector::IndexOutOfBounds(size_t maxSize, size_t indexAccessed) { + // VMSafe*Primitive::Invoke will push it right back to the same frame + VMFrame* frame = Interpreter::GetFrame(); + vm_oop_t errorMsg = Universe::NewString( + "Vector[1.." + std::to_string(maxSize) + "]: Index " + + std::to_string(indexAccessed) + " out of bounds"); + vm_oop_t args[1] = {errorMsg}; + this->Send("error:", args, 1); + return frame->Pop(); +} + +vm_oop_t VMVector::IndexOutOfBoundsAWFY() { + VMFrame* frame = Interpreter::GetFrame(); + this->Send("println", nullptr, 1); + return frame->Pop(); +} diff --git a/src/vmobjects/VMVector.h b/src/vmobjects/VMVector.h new file mode 100644 index 00000000..377bba57 --- /dev/null +++ b/src/vmobjects/VMVector.h @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include "../vm/Symbols.h" +#include "../vm/Universe.h" +#include "../vmobjects/VMInteger.h" +#include "../vmobjects/VMObject.h" +#include "ObjectFormats.h" +#include "VMArray.h" + +class VMVector : public VMObject { +public: + typedef GCVector Stored; + + explicit VMVector(vm_oop_t first, vm_oop_t last, VMArray* storage); + + /* Getter Methods */ + + /* handles 1 - 0 indexing give the SOM index to this function */ + [[nodiscard]] vm_oop_t GetStorage(int64_t index); + [[nodiscard]] vm_oop_t AWFYGetStorage(int64_t index); + + /* Return the first element */ + [[nodiscard]] inline vm_oop_t GetFirst() { + vm_oop_t returned = GetStorage(1); + return returned; + } + + /* Return the last element */ + [[nodiscard]] inline vm_oop_t GetLast() { + const int64_t last = INT_VAL(load_ptr(this->last)); + vm_oop_t returned = GetStorage(last - 1); + return returned; + } + + /* Setter Methods */ + + /* handles 1 - 0 indexing give the SOM index to this function */ + vm_oop_t SetStorage(int64_t index, vm_oop_t value); + void SetStorageAWFY(int64_t index, vm_oop_t value); + + /* Append an item to end of Vector */ + void Append(vm_oop_t value); + + /* Remove Methods */ + + vm_oop_t RemoveLast(); + + vm_oop_t RemoveFirst(); + + vm_oop_t RemoveObj(vm_oop_t other); + + vm_oop_t Remove(vm_oop_t inx); + + void RemoveAll(); + + /* General Utility Methods */ + + /* Size of the Vector */ + [[nodiscard]] inline vm_oop_t Size() { + const int64_t first = INT_VAL(load_ptr(this->first)); + const int64_t last = INT_VAL(load_ptr(this->last)); + return NEW_INT(last - first); + } + + [[nodiscard]] inline vm_oop_t Capacity() { + VMArray* storage = load_ptr(this->storage); + return NEW_INT(storage->GetNumberOfIndexableFields()); + } + + /* Return the underlying array */ + [[nodiscard]] vm_oop_t copyStorageArray(); + + vm_oop_t IndexOutOfBounds(size_t maxSize, size_t indexAccessed); + vm_oop_t IndexOutOfBoundsAWFY(); + +private: + static const size_t VMVectorNumberOfFields; + + gc_oop_t first; + gc_oop_t last; + GCArray* storage; +};