transform: fix bug in StringToBytes optimization pass
Previously, this pass would convert any read-only use of a runtime.stringToBytes call to use the original string buffer instead. This is incorrect: if there are any writes to the resulting buffer, none of the slice buffer pointers can be converted to use the original read-only string buffer. This commit fixes that bug and adds a test to prove the new (correct) behavior.
Этот коммит содержится в:
		
							родитель
							
								
									081d2e58c6
								
							
						
					
					
						коммит
						a896f7f218
					
				
					 3 изменённых файлов: 44 добавлений и 6 удалений
				
			
		|  | @ -28,32 +28,44 @@ func OptimizeStringToBytes(mod llvm.Module) { | |||
| 
 | ||||
| 		// strptr is always constant because strings are always constant. | ||||
| 
 | ||||
| 		convertedAllUses := true | ||||
| 		var pointerUses []llvm.Value | ||||
| 		canConvertPointer := true | ||||
| 		for _, use := range getUses(call) { | ||||
| 			if use.IsAExtractValueInst().IsNil() { | ||||
| 				// Expected an extractvalue, but this is something else. | ||||
| 				convertedAllUses = false | ||||
| 				canConvertPointer = false | ||||
| 				continue | ||||
| 			} | ||||
| 			switch use.Type().TypeKind() { | ||||
| 			case llvm.IntegerTypeKind: | ||||
| 				// A length (len or cap). Propagate the length value. | ||||
| 				// This can always be done because the byte slice is always the | ||||
| 				// same length as the original string. | ||||
| 				use.ReplaceAllUsesWith(strlen) | ||||
| 				use.EraseFromParentAsInstruction() | ||||
| 			case llvm.PointerTypeKind: | ||||
| 				// The string pointer itself. | ||||
| 				if !isReadOnly(use) { | ||||
| 					convertedAllUses = false | ||||
| 					// There is a store to the byte slice. This means that none | ||||
| 					// of the pointer uses can't be propagated. | ||||
| 					canConvertPointer = false | ||||
| 					continue | ||||
| 				} | ||||
| 				use.ReplaceAllUsesWith(strptr) | ||||
| 				use.EraseFromParentAsInstruction() | ||||
| 				// It may be that the pointer value can be propagated, if all of | ||||
| 				// the pointer uses are readonly. | ||||
| 				pointerUses = append(pointerUses, use) | ||||
| 			default: | ||||
| 				// should not happen | ||||
| 				panic("unknown return type of runtime.stringToBytes: " + use.Type().String()) | ||||
| 			} | ||||
| 		} | ||||
| 		if convertedAllUses { | ||||
| 		if canConvertPointer { | ||||
| 			// All pointer uses are readonly, so they can be converted. | ||||
| 			for _, use := range pointerUses { | ||||
| 				use.ReplaceAllUsesWith(strptr) | ||||
| 				use.EraseFromParentAsInstruction() | ||||
| 			} | ||||
| 
 | ||||
| 			// Call to runtime.stringToBytes can be eliminated: both the input | ||||
| 			// and the output is constant. | ||||
| 			call.EraseFromParentAsInstruction() | ||||
|  |  | |||
							
								
								
									
										16
									
								
								transform/testdata/stringtobytes.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										16
									
								
								transform/testdata/stringtobytes.ll
									
										
									
									
										предоставленный
									
									
								
							|  | @ -30,3 +30,19 @@ entry: | |||
|   call fastcc void @writeToSlice(ptr %1, i64 %2, i64 %3) | ||||
|   ret void | ||||
| } | ||||
| 
 | ||||
| ; Test that pointer values are never propagated if there is even a single write | ||||
| ; to the pointer value (but len/cap values still can be). | ||||
| define void @testReadSome() { | ||||
| entry: | ||||
|   %s = call fastcc { ptr, i64, i64 } @runtime.stringToBytes(ptr @str, i64 6) | ||||
|   %s.ptr = extractvalue { ptr, i64, i64 } %s, 0 | ||||
|   %s.len = extractvalue { ptr, i64, i64 } %s, 1 | ||||
|   %s.cap = extractvalue { ptr, i64, i64 } %s, 2 | ||||
|   call fastcc void @writeToSlice(ptr %s.ptr, i64 %s.len, i64 %s.cap) | ||||
|   %s.ptr2 = extractvalue { ptr, i64, i64 } %s, 0 | ||||
|   %s.len2 = extractvalue { ptr, i64, i64 } %s, 1 | ||||
|   %s.cap2 = extractvalue { ptr, i64, i64 } %s, 2 | ||||
|   call fastcc void @printSlice(ptr %s.ptr2, i64 %s.len2, i64 %s.cap2) | ||||
|   ret void | ||||
| } | ||||
|  |  | |||
							
								
								
									
										10
									
								
								transform/testdata/stringtobytes.out.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										10
									
								
								transform/testdata/stringtobytes.out.ll
									
										
									
									
										предоставленный
									
									
								
							|  | @ -22,3 +22,13 @@ entry: | |||
|   call fastcc void @writeToSlice(ptr %1, i64 6, i64 6) | ||||
|   ret void | ||||
| } | ||||
| 
 | ||||
| define void @testReadSome() { | ||||
| entry: | ||||
|   %s = call fastcc { ptr, i64, i64 } @runtime.stringToBytes(ptr @str, i64 6) | ||||
|   %s.ptr = extractvalue { ptr, i64, i64 } %s, 0 | ||||
|   call fastcc void @writeToSlice(ptr %s.ptr, i64 6, i64 6) | ||||
|   %s.ptr2 = extractvalue { ptr, i64, i64 } %s, 0 | ||||
|   call fastcc void @printSlice(ptr %s.ptr2, i64 6, i64 6) | ||||
|   ret void | ||||
| } | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem