diff --git a/.DS_Store b/.DS_Store index 3966a20..0f4d182 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.vscode/settings.json b/.vscode/settings.json index c5dfb4a..381bf7d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -61,7 +61,8 @@ "cstdio": "c", "cstdlib": "c", "cwchar": "c", - "new": "c" + "new": "c", + "param.h": "c" }, "C_Cpp.errorSquiggles": "disabled" } \ No newline at end of file diff --git a/KernelModule/BSDmakefile.meson b/KernelModule/BSDmakefile.meson new file mode 100644 index 0000000..6839ac0 --- /dev/null +++ b/KernelModule/BSDmakefile.meson @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +# makefile for building kernel modules using meson +# takes parameters from the environment + +# source file is passed via KMOD_SRC as relative path, we only use final +# (tail) component of it (:T), as VPATH is used to find actual file. The +# VPATH is similarly extracted from the non-final (head) portion of the +# path (:H) converted to absolute path (:tA). This use of VPATH is to have +# the .o files placed in the build, not source directory + +VPATH := ${KMOD_SRC:H:tA} +SRCS := ${KMOD_SRC:T} device_if.h bus_if.h pci_if.h +CFLAGS += $(KMOD_CFLAGS) +.OBJDIR: ${KMOD_OBJDIR} + +.include diff --git a/KernelModule/Hugepages/Makefile b/KernelModule/Hugepages/Makefile new file mode 100644 index 0000000..c8ff204 --- /dev/null +++ b/KernelModule/Hugepages/Makefile @@ -0,0 +1,5 @@ +KMOD= superpage +SRCS= superpage.c + +.include + diff --git a/KernelModule/Hugepages/build b/KernelModule/Hugepages/build new file mode 100644 index 0000000..e69de29 diff --git a/KernelModule/Hugepages/build.sh b/KernelModule/Hugepages/build.sh new file mode 100644 index 0000000..353a4c0 --- /dev/null +++ b/KernelModule/Hugepages/build.sh @@ -0,0 +1,6 @@ +cc -O2 -pipe -march=morello -Xclang -morello-vararg=new -Xclang -morello-bounded-memargs=caller-only -mabi=purecap-benchmark -D__LP64__=1 -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc -include /usr/home/akilan/Alloc-Test/CHERI-Allocator/KernelModule/Hugepages/opt_global.h -I. -I/usr/src/sys -I/usr/src/sys/contrib/ck/include -fno-common -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fPIC -fdebug-prefix-map=./machine=/usr/src/sys/arm64/include -MD -MF.depend.superpage.o -MTsuperpage.o -mgeneral-regs-only -ffixed-x18 -march=morello -Xclang -morello-vararg=new -Xclang -morello-bounded-memargs=caller-only -mabi=purecap -ffreestanding -fwrapv -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wcast-qual -Wundef -Wno-pointer-sign -D__printf__=__freebsd_kprintf__ -Wmissing-include-dirs -fdiagnostics-show-option -Wno-unknown-pragmas -Wno-error=tautological-compare -Wno-error=empty-body -Wno-error=parentheses-equality -Wno-error=unused-function -Wno-error=pointer-sign -Wno-error=shift-negative-value -Wno-address-of-packed-member -Wno-format-zero-length -Werror=implicit-function-declaration -std=gnu99 -c superpage.c -o superpage.o + +ld -m aarch64elf_cheri -d -warn-common --build-id=sha1 --no-relax --morello-c64-plt -r -o superpage.kld superpage.o +:> export_syms + +objcopy --strip-debug superpage.ko \ No newline at end of file diff --git a/KernelModule/Hugepages/meson.build b/KernelModule/Hugepages/meson.build new file mode 100644 index 0000000..7dd80ba --- /dev/null +++ b/KernelModule/Hugepages/meson.build @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +sources = files('superpage.c') diff --git a/KernelModule/Hugepages/superpage.c b/KernelModule/Hugepages/superpage.c new file mode 100644 index 0000000..f473eff --- /dev/null +++ b/KernelModule/Hugepages/superpage.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HUGE_PAGE_SIZE (2 * 1024 * 1024) // 2MB Huge Page + +static void *hugepage_ptr = NULL; + +// Load function - allocate memory +static int load_handler(module_t mod, int event, void *arg) { + switch (event) { + case MOD_LOAD: + printf("Loading Huge Page Kernel Module...\n"); + + // Allocate contiguous memory + hugepage_ptr = kmem_alloc_contig(HUGE_PAGE_SIZE, M_NOWAIT, + VM_MIN_KERNEL_ADDRESS, VM_MAX_KERNEL_ADDRESS, + HUGE_PAGE_SIZE, 0, + VM_MEMATTR_DEFAULT); + + if (hugepage_ptr == NULL) { + printf("Failed to allocate a huge page!\n"); + return ENOMEM; + } + + printf("Huge page allocated at %p\n", hugepage_ptr); + break; + + case MOD_UNLOAD: + printf("Unloading Huge Page Kernel Module...\n"); + + // Free memory if allocated + if (hugepage_ptr) { + kmem_free(hugepage_ptr, HUGE_PAGE_SIZE); + printf("Huge page freed.\n"); + } + break; + + default: + return EOPNOTSUPP; + } + return 0; +} + +// Register module +static moduledata_t superpage_mod = { + "superpage", // Module name + load_handler, + NULL +}; + +DECLARE_MODULE(superpage, superpage_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); + diff --git a/KernelModule/meson.build b/KernelModule/meson.build new file mode 100644 index 0000000..64e8660 --- /dev/null +++ b/KernelModule/meson.build @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +kmods = ['Hugepages'] + +# for building kernel modules, we use kernel build system using make, as +# with Linux. We have a skeleton BSDmakefile, which pulls many of its +# values from the environment. Each module only has a single source file +# right now, which allows us to simplify things. We pull in the sourcer +# files from the individual meson.build files, and then use a custom +# target to call make, passing in the values as env parameters. +kmod_cflags = ['-I' + dpdk_build_root, + '-I' + join_paths(dpdk_source_root, 'config'), + '-include rte_config.h'] + +kmod_arch = 'MACHINE_ARCH_NULL=' +kmod_arch = 'MACHINE_ARCH=aarch64' + +# to avoid warnings due to race conditions with creating the dev_if.h, etc. +# files, serialize the kernel module builds. Each module will depend on +# previous ones +built_kmods = [] +foreach k:kmods + subdir(k) + built_kmods += custom_target(k, + input: [files('BSDmakefile.meson'), sources], + output: k + '.ko', + command: ['make', '-f', '@INPUT0@', + 'KMOD_OBJDIR=@OUTDIR@', + 'KMOD_SRC=@INPUT1@', + 'KMOD=' + k, + 'KMOD_CFLAGS=' + ' '.join(kmod_cflags), + 'CC=clang', + kmod_arch], + depends: built_kmods, # make each module depend on prev + build_by_default: get_option('enable_kmods'), + install: get_option('enable_kmods'), + install_dir: '/boot/modules/') +endforeach \ No newline at end of file diff --git a/allocator/.DS_Store b/allocator/.DS_Store new file mode 100644 index 0000000..422445e Binary files /dev/null and b/allocator/.DS_Store differ diff --git a/allocator/bitmap/.DS_Store b/allocator/bitmap/.DS_Store new file mode 100644 index 0000000..f102e28 Binary files /dev/null and b/allocator/bitmap/.DS_Store differ diff --git a/benchmarks/.DS_Store b/benchmarks/.DS_Store index a68ebb2..fd13145 100644 Binary files a/benchmarks/.DS_Store and b/benchmarks/.DS_Store differ diff --git a/benchmarks/benchmarks/.DS_Store b/benchmarks/benchmarks/.DS_Store new file mode 100644 index 0000000..a5e97e1 Binary files /dev/null and b/benchmarks/benchmarks/.DS_Store differ diff --git a/benchmarks/benchmarks/kmeans/stddefines.h b/benchmarks/benchmarks/kmeans/stddefines.h index df2c653..3782f19 100644 --- a/benchmarks/benchmarks/kmeans/stddefines.h +++ b/benchmarks/benchmarks/kmeans/stddefines.h @@ -1,482 +1,54 @@ -/* Copyright (c) 2007-2009, Stanford University -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Stanford University nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY STANFORD UNIVERSITY ``AS IS'' AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL STANFORD UNIVERSITY BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef STDDEFINES_H_ -#define STDDEFINES_H_ - -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -// #include "bitmap.h" -#include "simple.h" - -#define MAXPAGESIZES 2 - - -// init_malloc(void) { -// // Init malloc implementation -// INITREGULARALLOC(); - -// // init_alloc(100,100000); -// // brm_init(); -// //alloc_init(104857632); -// } - -// void* MALLOC(size_t sz) { -// // malloc implementation -// return MALLOCCHERI(sz); -// // return malloc_buddy(sz); -// // return alloc(sz); -// // return (void *)alloc_chunk(); -// } - -// void FREE(void *ptr) { -// // free implementation -// FREECHERI(ptr); -// // free_chunk(ptr); -// //free_mem(ptr); -// } - - -//#define TIMING - -/* Debug printf */ -#define dprintf(...) fprintf(stdout, __VA_ARGS__) - -/* Wrapper to check for errors */ -#define CHECK_ERROR(a) \ - if (a) \ - { \ - perror("Error at line\n\t" #a "\nSystem Msg"); \ - assert ((a) == 0); \ - } - -// static inline void *MALLOC(size_t size) -// { -// void * temp = malloc(size); -// assert(temp); -// return temp; -// } - -static inline void *CALLOC(size_t num, size_t size) -{ - void * temp = calloc(num, size); - assert(temp); - return temp; -} - -static inline void *REALLOC(void *ptr, size_t size) -{ - void * temp = realloc(ptr, size); - assert(temp); - return temp; -} - -static inline char *GETENV(char *envstr) -{ - char *env = getenv(envstr); - if (!env) return "0"; - else return env; -} - -#define GET_TIME(start, end, duration) \ - duration.tv_sec = (end.tv_sec - start.tv_sec); \ - if (end.tv_nsec >= start.tv_nsec) { \ - duration.tv_nsec = (end.tv_nsec - start.tv_nsec); \ - } \ - else { \ - duration.tv_nsec = (1000000000L - (start.tv_nsec - end.tv_nsec)); \ - duration.tv_sec--; \ - } \ - if (duration.tv_nsec >= 1000000000L) { \ - duration.tv_sec++; \ - duration.tv_nsec -= 1000000000L; \ - } - -static inline unsigned int time_diff ( - struct timeval *end, struct timeval *begin) -{ -#ifdef TIMING - uint64_t result; - - result = end->tv_sec - begin->tv_sec; - result *= 1000000; /* usec */ - result += end->tv_usec - begin->tv_usec; - - return result; -#else - return 0; -#endif -} - -static inline void get_time (struct timeval *t) -{ -#ifdef TIMING - gettimeofday (t, NULL); -#endif -} - -// Expirement work - -#define FILENAME "/dev/contigmem" - -static char *heap_start; -static char *heap; -static size_t HEAP_SIZE = 1024 * 1024 * 1024; - -void *ptr; -int MallocCounter; - -size_t sizeUsed; - -// INITAlloc(void) { - -// size_t sz; -// // Pre Allocate 600 MB -// sz = 100000000; - -// int fd = open(FILENAME, O_RDWR, 0600); - -// if (fd < 0) { -// perror("open"); -// exit(EXIT_FAILURE); -// } - -// off_t offset = 0; // offset to seek to. - -// if (ftruncate(fd, sz) < 0) { -// perror("ftruncate"); -// close(fd); -// exit(EXIT_FAILURE); -// } - -// // ptr = mmap(NULL, sz, -// // PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0); - -// ptr = mmap(NULL, sz, -// PROT_READ|PROT_WRITE, MAP_SHARED,fd,0); - -// // Added error handling -// if(ptr == MAP_FAILED) -// { -// perror("mmap"); -// exit(EXIT_FAILURE); -// } - -// MallocCounter = (int)sz; - -// } - -// Quick malloc implementation with mmap -void* my_malloc(size_t sz) -{ - sz = __builtin_align_up(sz, _Alignof(max_align_t)); - - // printf("%d \n", sz); - // printf("%d Malloc counter\n", MallocCounter); - - MallocCounter -= sz; - void *ptrLink = &ptr[MallocCounter]; - ptrLink = cheri_setbounds(ptrLink, sz); - - return ptrLink; - -// if (heap + sz > heap_start + HEAP_SIZE) return NULL; -// heap += sz; -// return heap - sz; - -} - -// Quick cheri free implementation -void my_free(void *ptr) { - - // printf("free called \n"); - - // get bounds from - int len = cheri_getlen(ptr); - - // printf("free len %d \n", len); - - munmap(ptr, len); -} - -static int -pagesizes(size_t ps[MAXPAGESIZES]) -{ - int pscnt; - - pscnt = getpagesizes(ps, MAXPAGESIZES); - // ATF_REQUIRE_MSG(pscnt != -1, "getpagesizes failed; errno=%d", errno); - // ATF_REQUIRE_MSG(ps[0] != 0, "psind 0 is %zu", ps[0]); - // ATF_REQUIRE_MSG(pscnt <= MAXPAGESIZES, "invalid pscnt %d", pscnt); - // if (pscnt == 1){ - // printf("pscnt is 1"); - // } - - // atf_tc_skip("no large page support"); - return (pscnt); -} - -INITREGULARALLOC(void) { - size_t sz; - // Pre Allocate 400 MB - sz = 1073741824; - - int error, fd, pscnt, pn; - - size_t ps[MAXPAGESIZES]; - - size_t size[3]; - - pn = getpagesizes(size, 3); - printf("page size is [%d]", size[2]); - - pscnt = pagesizes(ps); - - fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, 1, SHM_LARGEPAGE_ALLOC_DEFAULT, 0); - - if (fd < 0 && errno == ENOTTY) { - perror("sh_create_largepages"); - close(fd); - exit(EXIT_FAILURE); - } - // if (fd < 0) - // perror("no large page supported"); - // exit(EXIT_FAILURE); - // if (fd < 0 && errno == ENOTTY) - // atf_tc_skip("no large page support"); - // ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; errno=%d", errno); - - if (ftruncate(fd, sz) < 0) { - perror("ftruncate"); - close(fd); - exit(EXIT_FAILURE); - } - // if (error != 0 && errno == ENOMEM) - // /* - // * The test system might not have enough memory to accommodate - // * the request. - // */ - // atf_tc_skip("failed to allocate %zu-byte superpage", sz); - // ATF_REQUIRE_MSG(error == 0, "ftruncate failed; errno=%d", errno); - - ptr = mmap(NULL, sz, - PROT_READ|PROT_WRITE, MAP_SHARED,fd,0); - - // Added error handling - if(ptr == MAP_FAILED) - { - perror("mmap"); - exit(EXIT_FAILURE); - } - - MallocCounter = (int)sz; -} - - -// ------------------------------ bitmap allocator ---------------------------- - -#define BITS_PER_BYTE 8 - -char *buffer = NULL; /* allocation buffer */ -unsigned char *bitmap = NULL; /* bitmap for the buffer */ - -int buffer_size = 0; /* size of buffer (in bytes) */ -int bitmap_size = 0; /* size of bitmap (in bytes) */ -int bytes_per_chunk = 0; /* size of single chunk (in bytes) */ - -void init_alloc(int num_chunks, int chunk_size) -{ - int i = 0; - - /* we need to increase the num_chunks - * so every bit in bitmap will be used - */ - int adjusted_num_chunks = (num_chunks % BITS_PER_BYTE == 0) - ? num_chunks - : (num_chunks + (BITS_PER_BYTE - (num_chunks % BITS_PER_BYTE))); - - /* we need to increase the chunk_size - * so chunks will be CHERI aligned - * (i.e. 16 bytes for RISC-V 64-bit arch) - */ - int adjusted_chunk_size = - (chunk_size % (sizeof(void *)) == 0) - ? chunk_size - : (chunk_size + (sizeof(void *)) - (chunk_size % (sizeof(void *)))); - - /* check this chunk size is small enough so we can represent - * bounds precisely with CHERI compressed representation - */ - adjusted_chunk_size = cheri_representable_length(adjusted_chunk_size); - - /* request memory for our allocation buffer */ - char *res = mmap(NULL, adjusted_num_chunks * adjusted_chunk_size, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, -1, 0); - /* request memory for our bitmap */ - bitmap = (void *) mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE, - PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - - if (res == MAP_FAILED || bitmap == MAP_FAILED) - { - perror("error in initial mem allocation"); - exit(-1); - } - - /* NB mmap min bounds for capability is 1 page (4K) */ - buffer = res; - /* check buffer is aligned */ - assert((uintptr_t) buffer % sizeof(void *) == 0); - /* check bitmap is aligned */ - assert((uintptr_t) bitmap % sizeof(void *) == 0); - - bytes_per_chunk = adjusted_chunk_size; - buffer_size = adjusted_num_chunks * adjusted_chunk_size; - bitmap_size = adjusted_num_chunks / BITS_PER_BYTE; - - /* zero bitmap, since all chunks are free initially */ - for (i = 0; i < bitmap_size; i++) - { - bitmap[i] = 0; - } - - // set exact bounds for buffer and bitmap? - buffer = cheri_setbounds(buffer, buffer_size); - bitmap = cheri_setbounds(bitmap, bitmap_size); - return; -} - -/* - * allocate fixed size chunk with bitmap allocator - * this is our simplistic `malloc` function - */ -char *alloc_chunk() -{ - unsigned char updated_byte = 0; - int chunk_index = 0; - char *chunk = NULL; - // iterate over all bits in bitmap, looking for a 0 - // when we find a 0, set it to 1 and - // return the corresponding chunk - // (setting its capability bounds) - int i = 0; - while (bitmap[i] == (unsigned char) 0xff) - { - i++; - if (i >= bitmap_size) +#include +#include +#include +#include +#include +#include + +#include // ✅ Defines vm_size_t, vm_offset_t +#include // ✅ Extra VM-related macros +#include +#include // ✅ Must be included AFTER + +#define HUGE_PAGE_SIZE (2 * 1024 * 1024) // 2MB Large Page + +static void *hugepage_ptr = NULL; + +static int load_handler(module_t mod, int event, void *arg) { + switch (event) { + case MOD_LOAD: + printf("Loading Huge Page Kernel Module...\n"); + + hugepage_ptr = kmem_alloc_contig(HUGE_PAGE_SIZE, M_WAITOK, + 0, BUS_SPACE_MAXADDR, + HUGE_PAGE_SIZE, 0, + VM_MEMATTR_DEFAULT); + + if (!hugepage_ptr) { + printf("Failed to allocate a huge page!\n"); + return ENOMEM; + } + printf("Huge page allocated at %p\n", hugepage_ptr); break; - } - // do we have a 0? - if (i < bitmap_size && bitmap[i] != (unsigned char) 0xff) - { - // find the lowest 0 ... - int j = 0; - // right shift until bottom bit is 0 - for (j = 0; j < BITS_PER_BYTE; j++) - { - int bit = (bitmap[i] >> j) & 1; - if (bit == 0) - { - break; + + case MOD_UNLOAD: + if (hugepage_ptr) { + kmem_free(hugepage_ptr, HUGE_PAGE_SIZE); + printf("Huge page freed.\n"); } - } - // now i is the word index, j is the bit index - // set this bit to 1 ... - // and work out the chunk to allocate - updated_byte = bitmap[i] + (unsigned char) (1 << j); - bitmap[i] = updated_byte; + printf("Unloading module...\n"); + break; - chunk_index = i * BITS_PER_BYTE + j; - chunk = buffer + (chunk_index * bytes_per_chunk); - - /* restrict capability range before returning ptr */ - chunk = cheri_setbounds(chunk, bytes_per_chunk); + default: + return EOPNOTSUPP; } - - return chunk; + return 0; } -void free_chunk(void *chunk) -{ - vaddr_t base = cheri_getbase(chunk); - vaddr_t buff_base = cheri_getbase(buffer); - /* calculate chunk index in buffer */ - int chunk_index = (base - buff_base) / bytes_per_chunk; - assert(chunk_index >= 0); - /* calculate corresponding bitmap index */ - int bitmap_index = chunk_index / BITS_PER_BYTE; - assert(bitmap_index < bitmap_size); - int bitmap_offset = chunk_index % BITS_PER_BYTE; - /* set this bitmap entry to 0 */ - unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char) (~(1 << bitmap_offset)); - bitmap[bitmap_index] = updated_byte; - return; -} +static moduledata_t superpage_mod = { + "superpage", + load_handler, + NULL +}; -int num_used_chunks() -{ - int i = 0; - int used_chunks = 0; - - while (i < bitmap_size) - { - unsigned char x = bitmap[i]; - if (x != 0) - { - /* some used chunks here */ - unsigned char j; - for (j = 1; j <= x; j = j << 1) - { - if (x & j) - { - used_chunks++; - } - } - } - i++; - } - return used_chunks; -} - -#endif // STDDEFINES_H_ \ No newline at end of file +DECLARE_MODULE(superpage, superpage_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); diff --git a/docs/.DS_Store b/docs/.DS_Store index 16d35d8..6bf58a1 100644 Binary files a/docs/.DS_Store and b/docs/.DS_Store differ diff --git a/docs/EuroSys/.DS_Store b/docs/EuroSys/.DS_Store new file mode 100644 index 0000000..3c1ac53 Binary files /dev/null and b/docs/EuroSys/.DS_Store differ diff --git a/docs/EuroSys/ACM-Reference-Format.bst b/docs/EuroSys/ACM-Reference-Format.bst new file mode 100644 index 0000000..c47cb4c --- /dev/null +++ b/docs/EuroSys/ACM-Reference-Format.bst @@ -0,0 +1,3081 @@ +%%% -*-BibTeX-*- +%%% ==================================================================== +%%% @BibTeX-style-file{ +%%% author = "Nelson H. F. Beebe, Boris Veytsman and Gerald Murray", +%%% version = "2.1", +%%% acmart-version = "1.90", +%%% date = "Mar 26 2023", +%%% filename = "ACM-Reference-Format.bst", +%%% email = "borisv@lk.net, boris@varphi.com", +%%% codetable = "ISO/ASCII", +%%% keywords = "ACM Transactions bibliography style; BibTeX", +%%% license = "public domain", +%%% supported = "yes", +%%% abstract = "", +%%% } +%%% ==================================================================== + +%%% Revision history: see source in git + +ENTRY + { address + advisor + archiveprefix + author + booktitle + chapter + city + date + edition + editor + eprint + eprinttype + eprintclass + howpublished + institution + journal + key + location + month + note + number + organization + pages + primaryclass + publisher + school + series + title + type + volume + year + % New keys recognized + issue % UTAH: used in, e.g., ACM SIGSAM Bulletin and ACM Communications in Computer Algebra + articleno + eid + day % UTAH: needed for newspapers, weeklies, bi-weeklies + doi % UTAH + url % UTAH + bookpages % UTAH + numpages + lastaccessed % UTAH: used only for @Misc{...} + coden % UTAH + isbn % UTAH + isbn-13 % UTAH + issn % UTAH + lccn % UTAH + distinctURL % whether to print url if doi is present + } + {} + { label.year extra.label sort.year sort.label basic.label.year} + +INTEGERS { output.state before.all mid.sentence after.sentence after.block } + +INTEGERS { show-isbn-10-and-13 } % initialized below in begin.bib + +INTEGERS { nameptr namesleft numnames } + +INTEGERS { multiresult } + +INTEGERS { len } + +INTEGERS { last.extra.num } + +STRINGS { s t t.org u } + +STRINGS { last.label next.extra } + +STRINGS { p1 p2 p3 page.count } + + +FUNCTION { not } +{ + { #0 } + { #1 } + if$ +} + +FUNCTION { and } +{ + 'skip$ + { pop$ #0 } + if$ +} + +FUNCTION { or } +{ + { pop$ #1 } + 'skip$ + if$ +} + + +FUNCTION { dump.stack.1 } +{ + duplicate$ "STACK[top] = [" swap$ * "]" * warning$ +} + +FUNCTION { dump.stack.2 } +{ + duplicate$ "STACK[top ] = [" swap$ * "]" * warning$ + swap$ + duplicate$ "STACK[top-1] = [" swap$ * "]" * warning$ + swap$ +} + +FUNCTION { empty.or.unknown } +{ + %% Examine the top stack entry, and push 1 if it is empty, or + %% consists only of whitespace, or is a string beginning with two + %% queries (??), and otherwise, push 0. + %% + %% This function provides a replacement for empty$, with the + %% convenient feature that unknown values marked by two leading + %% queries are treated the same as missing values, and thus, do not + %% appear in the output .bbl file, and yet, their presence in .bib + %% file(s) serves to mark values which are temporarily missing, but + %% are expected to be filled in eventually once more data is + %% obtained. The TeX User Group and BibNet bibliography archives + %% make extensive use of this practice. + %% + %% An empty string cannot serve the same purpose, because just as in + %% statistics data processing, an unknown value is not the same as an + %% empty value. + %% + %% At entry: stack = ... top:[string] + %% At exit: stack = ... top:[0 or 1] + + duplicate$ empty$ + { pop$ #1 } + { #1 #2 substring$ "??" = } + if$ +} + +FUNCTION { empty.or.zero } +{ + %% Examine the top entry and push 1 if it is empty, or is zero + duplicate$ empty$ + { pop$ #1 } + { "0" = } + if$ +} + + +FUNCTION { writeln } +{ + %% In BibTeX style files, the sequences + %% + %% ... "one" "two" output + %% ... "one" "two" output.xxx + %% + %% ship "one" to the output file, possibly following by punctuation, + %% leaving the stack with + %% + %% ... "two" + %% + %% There is thus a one-string lag in output processing that must be + %% carefully handled to avoid duplicating a string in the output + %% file. Unless otherwise noted, all output.xxx functions leave + %% just one new string on the stack, and that model should be born + %% in mind when reading or writing function code. + %% + %% BibTeX's asynchronous buffering of output from strings from the + %% stack is confusing because newline$ bypasses the buffer. It + %% would have been so much easier for newline to be a character + %% rather than a state of the output-in-progress. + %% + %% The documentation in btxhak.dvi is WRONG: it says + %% + %% newline$ Writes onto the bbl file what's accumulated in the + %% output buffer. It writes a blank line if and only + %% if the output buffer is empty. Since write$ does + %% reasonable line breaking, you should use this + %% function only when you want a blank line or an + %% explicit line break. + %% + %% write$ Pops the top (string) literal and writes it on the + %% output buffer (which will result in stuff being + %% written onto the bbl file when the buffer fills + %% up). + %% + %% Examination of the BibTeX source code shows that write$ does + %% indeed behave as claimed, but newline$ sends a newline character + %% directly to the output file, leaving the stack unchanged. The + %% first line "Writes onto ... buffer." is therefore wrong. + %% + %% The original BibTeX style files almost always use "write$ newline$" + %% in that order, so it makes sense to hide that pair in a private + %% function like this one, named after a statement in Pascal, + %% the programming language embedded in the BibTeX Web program. + + write$ % output top-of-stack string + newline$ % immediate write of newline (not via stack) +} + +FUNCTION { init.state.consts } +{ + #0 'before.all := + #1 'mid.sentence := + #2 'after.sentence := + #3 'after.block := +} + +FUNCTION { output.nonnull } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = + { + ", " * write$ + } + { + output.state after.block = + { + add.period$ writeln + "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output.nonnull.dot.space } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = % { ". " * write$ } + { + ". " * write$ + } + { + output.state after.block = + { + add.period$ writeln "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output.nonnull.remove } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = + { + " " * write$ + } + { + output.state after.block = + { + add.period$ writeln "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output.nonnull.removenospace } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = + { + "" * write$ + } + { + output.state after.block = + { + add.period$ writeln "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output } +{ % discard top token if empty, else like output.nonnull + duplicate$ empty.or.unknown + 'pop$ + 'output.nonnull + if$ +} + +FUNCTION { output.dot.space } +{ % discard top token if empty, else like output.nonnull.dot.space + duplicate$ empty.or.unknown + 'pop$ + 'output.nonnull.dot.space + if$ +} + +FUNCTION { output.removenospace } +{ % discard top token if empty, else like output.nonnull.removenospace + duplicate$ empty.or.unknown + 'pop$ + 'output.nonnull.removenospace + if$ +} + +FUNCTION { output.check } +{ % like output, but warn if key name on top-of-stack is not set + 't := + duplicate$ empty.or.unknown + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull + if$ +} + +FUNCTION { bibinfo.output.check } +{ % like output.check, adding bibinfo field + 't := + duplicate$ empty.or.unknown + { pop$ "empty " t * " in " * cite$ * warning$ } + { "\bibinfo{" t "}{" * * swap$ * "}" * + output.nonnull } + if$ +} + +FUNCTION { output.check.dot.space } +{ % like output.dot.space, but warn if key name on top-of-stack is not set + 't := + duplicate$ empty.or.unknown + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull.dot.space + if$ +} + +FUNCTION { fin.block } +{ % functionally, but not logically, identical to fin.entry + add.period$ + writeln +} + +FUNCTION { fin.entry } +{ + add.period$ + writeln +} + +FUNCTION { new.sentence } +{ % update sentence state, with neither output nor stack change + output.state after.block = + 'skip$ + { + output.state before.all = + 'skip$ + { after.sentence 'output.state := } + if$ + } + if$ +} + +FUNCTION { fin.sentence } +{ + add.period$ + write$ + new.sentence + "" +} + +FUNCTION { new.block } +{ + output.state before.all = + 'skip$ + { after.block 'output.state := } + if$ +} + +FUNCTION { output.coden } % UTAH +{ % output non-empty CODEN as one-line sentence (stack untouched) + coden empty.or.unknown + { } + { "\showCODEN{" coden * "}" * writeln } + if$ +} + +% +% Sometimes articleno starts with the word 'Article' or 'Paper. +% (this is a bug of acmdl, sigh) +% We strip them. We assume eid or articleno is already on stack +% + +FUNCTION { strip.articleno.or.eid } +{ + 't := + t #1 #7 substring$ "Article" = + {t #8 t text.length$ substring$ 't :=} + { } + if$ + t #1 #7 substring$ "article" = + {t #8 t text.length$ substring$ 't :=} + { } + if$ + t #1 #5 substring$ "Paper" = + {t #6 t text.length$ substring$ 't :=} + { } + if$ + t #1 #5 substring$ "paper" = + {t #6 t text.length$ substring$ 't :=} + { } + if$ + % Strip any left trailing space or ~ + t #1 #1 substring$ " " = + {t #2 t text.length$ substring$ 't :=} + { } + if$ + t #1 #1 substring$ "~" = + {t #2 t text.length$ substring$ 't :=} + { } + if$ + t +} + + +FUNCTION { format.articleno } +{ + articleno empty.or.unknown not eid empty.or.unknown not and + { "Both articleno and eid are defined for " cite$ * warning$ } + 'skip$ + if$ + articleno empty.or.unknown eid empty.or.unknown and + { "" } + { + numpages empty.or.unknown + { "articleno or eid field, but no numpages field, in " + cite$ * warning$ } + { } + if$ + eid empty.or.unknown + { "Article \bibinfo{articleno}{" articleno strip.articleno.or.eid * "}" * } + { "Article \bibinfo{articleno}{" eid strip.articleno.or.eid * "}" * } + if$ + } + if$ +} + +FUNCTION { format.year } +{ % push year string or "[n.\,d.]" onto output stack + %% Because year is a mandatory field, we always force SOMETHING + %% to be output + "\bibinfo{year}{" + year empty.or.unknown + { "[n.\,d.]" } + { year } + if$ + * "}" * +} + +FUNCTION { format.day.month } +{ % push "day month " or "month " or "" onto output stack + day empty.or.unknown + { + month empty.or.unknown + { "" } + { "\bibinfo{date}{" month * "} " *} + if$ + } + { + month empty.or.unknown + { "" } + { "\bibinfo{date}{" day * " " * month * "} " *} + if$ + } + if$ +} + +FUNCTION { format.day.month.year } % UTAH +{ % if month is empty, push "" else push "(MON.)" or "(DD MON.)" + % Needed for frequent periodicals: 2008. ... New York Times C-1, C-2, C-17 (23 Oct.) + % acm-*.bst addition: prefix parenthesized date string with + % ", Article nnn " + articleno empty.or.unknown eid empty.or.unknown and + { "" } + { output.state after.block = + {", " format.articleno * } + { format.articleno } + if$ + } + if$ + " (" * format.day.month * format.year * ")" * +} + +FUNCTION { output.day.month.year } % UTAH +{ % if month is empty value, do nothing; else output stack top and + % leave with new top string "(MON.)" or "(DD MON.)" + % Needed for frequent periodicals: 2008. ... New York Times C-1, C-2, C-17 (23 Oct.) + format.day.month.year + output.nonnull.remove +} + +FUNCTION { strip.doi } % UTAH +{ % Strip any Web address prefix to recover the bare DOI, leaving the + % result on the output stack, as recommended by CrossRef DOI + % documentation. + % For example, reduce "http://doi.acm.org/10.1145/1534530.1534545" to + % "10.1145/1534530.1534545". A suitable URL is later typeset and + % displayed as the LAST item in the reference list entry. Publisher Web + % sites wrap this with a suitable link to a real URL to resolve the DOI, + % and the master https://doi.org/ address is preferred, since publisher- + % specific URLs can disappear in response to economic events. All + % journals are encouraged by the DOI authorities to use that typeset + % format and link procedures for uniformity across all publications that + % include DOIs in reference lists. + % The numeric prefix is guaranteed to start with "10.", so we use + % that as a test. + % 2017-02-04 Added stripping of https:// (Boris) + doi #1 #3 substring$ "10." = + { doi } + { + doi 't := % get modifiable copy of DOI + + % Change https:// to http:// to strip both prefixes (BV) + + t #1 #8 substring$ "https://" = + { "http://" t #9 t text.length$ #8 - substring$ * 't := } + { } + if$ + + t #1 #7 substring$ "http://" = + { + t #8 t text.length$ #7 - substring$ 't := + + "INTERNAL STYLE-FILE ERROR" 's := + + % search for next "/" and assign its suffix to s + + { t text.length$ } + { + t #1 #1 substring$ "/" = + { + % save rest of string as true DOI (should be 10.xxxx/yyyy) + t #2 t text.length$ #1 - substring$ 's := + "" 't := % empty string t terminates the loop + } + { + % discard first character and continue loop: t <= substring(t,2,last) + t #2 t text.length$ #1 - substring$ 't := + } + if$ + } + while$ + + % check for valid DOI (should be 10.xxxx/yyyy) + s #1 #3 substring$ "10." = + { } + { "unrecognized DOI substring " s * " in DOI value [" * doi * "]" * warning$ } + if$ + + s % push the stripped DOI on the output stack + + } + { + "unrecognized DOI value [" doi * "]" * warning$ + doi % push the unrecognized original DOI on the output stack + } + if$ + } + if$ +} + +% +% Change by BV: added standard prefix to URL +% +FUNCTION { output.doi } % UTAH +{ % output non-empty DOI as one-line sentence (stack untouched) + doi empty.or.unknown + { } + { + %% Use \urldef here for the same reason it is used in output.url, + %% see output.url for further discussion. + "\urldef\tempurl%" writeln + "\url{https://doi.org/" strip.doi * "}" * writeln + "\showDOI{\tempurl}" writeln + } + if$ +} + +FUNCTION { output.isbn } % UTAH +{ % output non-empty ISBN-10 and/or ISBN-13 as one-line sentences (stack untouched) + show-isbn-10-and-13 + { + %% show both 10- and 13-digit ISBNs + isbn empty.or.unknown + { } + { + "\showISBNx{" isbn * "}" * writeln + } + if$ + isbn-13 empty.or.unknown + { } + { + "\showISBNxiii{" isbn-13 * "}" * writeln + } + if$ + } + { + %% show 10-digit ISBNs only if 13-digit ISBNs not available + isbn-13 empty.or.unknown + { + isbn empty.or.unknown + { } + { + "\showISBNx{" isbn * "}" * writeln + } + if$ + } + { + "\showISBNxiii{" isbn-13 * "}" * writeln + } + if$ + } + if$ +} + +FUNCTION { output.issn } % UTAH +{ % output non-empty ISSN as one-line sentence (stack untouched) + issn empty.or.unknown + { } + { "\showISSN{" issn * "}" * writeln } + if$ +} + +FUNCTION { output.issue } +{ % output non-empty issue number as a one-line sentence (stack untouched) + issue empty.or.unknown + { } + { "Issue " issue * "." * writeln } + if$ +} + +FUNCTION { output.lccn } % UTAH +{ % return with stack untouched + lccn empty.or.unknown + { } + { "\showLCCN{" lccn * "}" * writeln } + if$ +} + +FUNCTION { output.note } % UTAH +{ % return with stack empty + note empty.or.unknown + { } + { "\shownote{" note * "}" add.period$ * writeln } + if$ +} + +FUNCTION { output.note.check } % UTAH +{ % return with stack empty + note empty.or.unknown + { "empty note in " cite$ * warning$ } + { "\shownote{" note * "}" add.period$ * writeln } + if$ +} + +FUNCTION { output.eprint } % +{ % return with stack empty + eprint empty.or.unknown + { } + { "\showeprint" + archiveprefix empty.or.unknown + { eprinttype empty.or.unknown + { } + { "[" eprinttype "]" * * * } + if$ + } + { "[" archiveprefix "l" change.case$ "]" * * * } + if$ + "{" eprint "}" * * * + primaryclass empty.or.unknown + { eprintclass empty.or.unknown + { } + { "~[" eprintclass "]" * * * } + if$ + } + { "~[" primaryclass "]" * * * } + if$ + writeln + } + if$ +} + + +% +% Changes by BV 2011/04/15. Do not output +% url if doi is defined +% +% +% Changes by BV 2021/11/26. Output url even if doi is defined +% if distinctURL is not zero. +% +FUNCTION { output.url } % UTAH +{ % return with stack untouched + % output URL and associated lastaccessed fields + doi empty.or.unknown distinctURL empty.or.zero not or + { + url empty.or.unknown + { } + { + %% Use \urldef, outside \showURL, so that %nn, #, etc in URLs work + %% correctly. Put the actual URL on its own line to reduce the + %% likelihood of BibTeX's nasty line wrapping after column 79. + %% \url{} can undo this, but if that doesn't work for some reason + %% the .bbl file would have to be repaired manually. + "\urldef\tempurl%" writeln + "\url{" url * "}" * writeln + + "\showURL{%" writeln + lastaccessed empty.or.unknown + { "" } + { "Retrieved " lastaccessed * " from " * } + if$ + "\tempurl}" * writeln + } + if$ + } + { } + if$ +} + +FUNCTION { output.year.check } +{ % warn if year empty, output top string and leave " YEAR