Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ set(LLHTTP_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/api.c
)

# On x86-64, compile a second instantiation of the parser with SSE4.2 enabled so
# llhttp_execute() can select it at runtime (see src/api.c). The default build
# passes no -march, so the SSE4.2 span scanners in the generated code are
# otherwise never compiled in. Skipped on MSVC (no __builtin_cpu_supports).
if(NOT MSVC AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|AMD64)$")
list(APPEND LLHTTP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/llhttp_sse42.c)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/src/llhttp_sse42.c
PROPERTIES COMPILE_OPTIONS "-msse4.2"
)
endif()

set(LLHTTP_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/llhttp.h
)
Expand Down
20 changes: 17 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ endif

INCLUDES += -Ibuild/

# On x86-64, also build a second instantiation of the generated parser with
# -msse4.2 so llhttp_execute() can select the SSE4.2 span scanners at runtime
# (see src/native/api.c). The default build passes no -march, so without this
# the SSE4.2 code generated under `#ifdef __SSE4_2__` is never compiled in.
ARCH ?= $(shell uname -m)
ifneq (,$(filter $(ARCH),x86_64 amd64 AMD64))
SSE42_OBJ = build/c/llhttp_sse42.o
endif

INSTALL ?= install
PREFIX ?= /usr/local
LIBDIR = $(PREFIX)/lib
Expand All @@ -23,17 +32,20 @@ clean:
rm -rf build/
rm -rf test/tmp

build/libllhttp.so: build/c/llhttp.o build/native/api.o \
build/libllhttp.so: build/c/llhttp.o $(SSE42_OBJ) build/native/api.o \
build/native/http.o
$(CLANG) -shared $^ -o $@

build/libllhttp.a: build/c/llhttp.o build/native/api.o \
build/libllhttp.a: build/c/llhttp.o $(SSE42_OBJ) build/native/api.o \
build/native/http.o
$(AR) rcs $@ build/c/llhttp.o build/native/api.o build/native/http.o
$(AR) rcs $@ build/c/llhttp.o $(SSE42_OBJ) build/native/api.o build/native/http.o

build/c/llhttp.o: build/c/llhttp.c
$(CLANG) $(CFLAGS) $(INCLUDES) -c $< -o $@

build/c/llhttp_sse42.o: build/c/llhttp_sse42.c build/c/llhttp.c
$(CLANG) $(CFLAGS) -msse4.2 $(INCLUDES) -c $< -o $@

build/native/%.o: src/native/%.c build/llhttp.h src/native/api.h \
build/native
$(CLANG) $(CFLAGS) $(INCLUDES) -c $< -o $@
Expand All @@ -52,6 +64,8 @@ release: clean all generate
mkdir -p release/include
cp -rf build/llhttp.h release/include/
cp -rf build/c/llhttp.c release/src/
cp -rf build/c/llhttp_sse42.c release/src/
cp -rf build/c/llhttp_sse42_rename.h release/src/
cp -rf src/native/*.c release/src/
cp -rf src/llhttp.gyp release/
cp -rf src/common.gypi release/
Expand Down
31 changes: 31 additions & 0 deletions bin/generate.ts
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const semverRE = /^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\

const C_FILE = resolve(__dirname, '../build/c/llhttp.c');
const HEADER_FILE = resolve(__dirname, '../build/llhttp.h');
const SSE42_RENAME_FILE = resolve(__dirname, '../build/c/llhttp_sse42_rename.h');
const SSE42_C_FILE = resolve(__dirname, '../build/c/llhttp_sse42.c');

const pkg = JSON.parse(
readFileSync(resolve(__dirname, '..', 'package.json')).toString(),
Expand Down Expand Up @@ -49,3 +51,32 @@ ${nativeHeaders}
mkdirSync(dirname(C_FILE), { recursive: true });
writeFileSync(HEADER_FILE, headers);
writeFileSync(C_FILE, generated.c);

// A second instantiation of the generated parser, compiled with -msse4.2, lets
// llhttp_execute() use the SSE4.2 span scanners at runtime on default x86-64
// builds (which pass no -march, so the `#ifdef __SSE4_2__` code is otherwise
// dead). The parser defines symbols prefixed `llhttp__internal`; the callbacks
// it imports are prefixed `llhttp__on`/`llhttp__before`/`llhttp__after` and stay
// shared. Rename only the defined `llhttp__internal*` symbols so the second copy
// links beside the first. Derived from the generated source, so it stays correct
// across grammar changes.
const internalSymbols = Array.from(
new Set(generated.c.match(/\bllhttp__internal[0-9A-Za-z_]*/g) ?? []),
).sort();
const renameHeader = `/* Generated by bin/generate.ts. Renames the defined symbols of llhttp.c so a
* second, -msse4.2 instantiation (llhttp_sse42.c) can link beside the first. */
#ifndef INCLUDE_LLHTTP_SSE42_RENAME_H_
#define INCLUDE_LLHTTP_SSE42_RENAME_H_
${internalSymbols.map((s) => `#define ${s} ${s}_sse42`).join('\n')}
#endif /* INCLUDE_LLHTTP_SSE42_RENAME_H_ */
`;
const sse42Wrapper = `/* Generated by bin/generate.ts. See src/native/api.c: a second instantiation of
* the generated parser with the SSE4.2 span scanners enabled, selected once at
* runtime by llhttp_execute(). Built with -msse4.2 by the build system. */
#if defined(__x86_64__) || defined(_M_X64)
#include "llhttp_sse42_rename.h"
#include "llhttp.c"
#endif
`;
writeFileSync(SSE42_RENAME_FILE, renameHeader);
writeFileSync(SSE42_C_FILE, sse42Wrapper);
24 changes: 24 additions & 0 deletions src/native/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,33 @@ void llhttp_reset(llhttp_t* parser) {
}


#if (defined(__x86_64__) || defined(_M_X64)) && !defined(__SSE4_2__) && \
(defined(__GNUC__) || defined(__clang__))
/* A second instantiation of the generated parser, compiled with -msse4.2 (see
* llhttp_sse42.c), which turns on the SSE4.2 span scanners that the generated
* code guards with `#ifdef __SSE4_2__`. The default x86-64 build (no -march)
* never contains them, so pick the SSE4.2 build once at runtime. Both
* instantiations come from the same generated source, so the parser state
* layout is identical and a parser may be driven by either. */
llhttp_errno_t llhttp__internal_execute_sse42(llhttp_t* state, const char* p,
const char* endp);

static int llhttp__sse42 = -1;

llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
if (llhttp__sse42 < 0) {
llhttp__sse42 = __builtin_cpu_supports("sse4.2") ? 1 : 0;
}
if (llhttp__sse42) {
return llhttp__internal_execute_sse42(parser, data, data + len);
}
return llhttp__internal_execute(parser, data, data + len);
}
#else
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
return llhttp__internal_execute(parser, data, data + len);
}
#endif


void llhttp_settings_init(llhttp_settings_t* settings) {
Expand Down